phantomjs
是无头浏览器的代表,可以截全屏的图,对于标记元素来说是很简单的;不过最新的 Selenium
版本表示不再支持;所以只能使用其他的代理品; 正好 chrome
,Firefox
等都推出无头模式,这里就使用 ChromeDriver
作为演示
ChromeDriver
通过设置 setHeadless(true)
既可开启无头模式
设置 ChromeDirver
必要的参数
//-------------------------
// 该方法有同事提供 -> 滑稽脸
//-------------------------
private static ChromeOptions initWebOption(String proxy) {
ChromeOptions chromeOptions = new ChromeOptions();
// 开启无头模式
chromeOptions.setHeadless(true);
//基础参数设置
chromeOptions.addArguments("--silent");
chromeOptions.addArguments("--no-sandbox");
chromeOptions.addArguments("--disable-gpu");
chromeOptions.addArguments("--disable-dev-shm-usage");
chromeOptions.addArguments("--ignore-certificate-errors");
chromeOptions.addArguments("--allow-running-insecure-content");
//优化参数设置
chromeOptions.addArguments("--incognito");
chromeOptions.addArguments("--disable-images");
chromeOptions.addArguments("--start-maximized");
chromeOptions.addArguments("--disable-plugins");
chromeOptions.addArguments("--disable-infobars");
chromeOptions.addArguments("--lang=zh_CN.UTF-8");
chromeOptions.addArguments("--disable-javascript");
chromeOptions.addArguments("--window-size=1928,1080");
//chromeOptions.addArguments("--auto-open-devtools-for-tabs");
//UserAgent
String userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.3239.132 Safari/537.36";
chromeOptions.addArguments("--user-agent=" + userAgent);
log.info("[初始驱动参数] 正在设置驱动用户代理: {}", userAgent);
//设置加载策略
String pageLoadStrategy = "eager";
chromeOptions.setCapability("pageLoadStrategy", pageLoadStrategy);
log.info("[初始驱动参数] 正在设置驱动加载策略: {}", pageLoadStrategy);
//高级参数设置
Map<String, Object> chromePrefs = new HashMap<>();
//禁止密码保存
chromePrefs.put("credentials_enable_service", false);
chromePrefs.put("profile.password_manager_enabled", false);
//禁止网页弹窗
chromePrefs.put("profile.default_content_settings.popups", 0);
//禁止加载图片
chromePrefs.put("profile.managed_default_content_settings.images", 2);
chromeOptions.setExperimentalOption("prefs", chromePrefs);
//屏蔽控制显示
chromeOptions.setExperimentalOption("useAutomationExtension", false);
//突破网站检测
chromeOptions.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
//网络代理设置
if (proxy == null || proxy.length() == 0) {
return chromeOptions;
}
Proxy netProxy = new Proxy();
netProxy.setHttpProxy(proxy);
chromeOptions.setProxy(netProxy);
return chromeOptions;
}
截屏并标记特定元素
public void screenshotAndMark(
// Chrome Driver
WebDriver webDriver,
// 同一个页面需要标记的元素
List<WebElement> elements,
// 自定义截图的名称
Function<WebElement, String> nameMapping,
// 接受最后的结果
Consumer<Optional<List<ScreenshotMark>>> consumer
) {
if (webDriver == null) {
logger.warn("WebDriver is null and screenshot failed.");
if (consumer != null) {
consumer.accept(Optional.empty());
}
return;
}
if (elements == null || elements.isEmpty()) {
logger.warn("elements is empty and screenshot failed.");
if (consumer != null) {
consumer.accept(Optional.empty());
}
return;
}
// JS 脚本执行器
JavascriptExecutor executor = ((JavascriptExecutor) webDriver);
// 结果
List<ScreenshotMark> screenshotMarks = new ArrayList<>();
for (WebElement webElement : elements) {
// 元素原始位置
Point location = webElement.getLocation();
int x = location.x;
int y = location.y;
// 滚动元素到可视区域
// 并设置误差 10px
executor.executeScript("window.scrollTo(arguments[0], arguments[1])", x - 10, y - 10);
// 元素大小
Dimension size = webElement.getSize();
int width = size.getWidth();
int height = size.getHeight();
String name = nameMapping != null ? nameMapping.apply(webElement) : webElement.getText();
if (width == 0 || height == 0) {
System.out.println(name + " width eq zero or height eq zero, ignored.");
continue;
}
if (!webElement.isDisplayed()) {
System.out.println(name + " is not display, ignored.");
continue;
}
// 获取当前滚动条的位置
Object offsetYObj = executor.executeScript("return document.body.scrollTop || document.documentElement.scrollTop");
Object offsetXObj = executor.executeScript("return document.body.scrollLeft || document.documentElement.scrollLeft");
// 计算元素的偏移位置
int offsetY = 0;
int offsetX = 0;
if (offsetYObj != null) {
try {
offsetY = Integer.parseInt(offsetYObj.toString());
} catch (Exception e) {
// ignore
}
}
if (offsetXObj != null) {
try {
offsetX = Integer.parseInt(offsetXObj.toString());
} catch (Exception e) {
// ignore
}
}
WebDriver augmentedDriver = new Augmenter().augment(webDriver);
// 不同元素展示的结果不同
// 所以每一次都需要截屏
byte[] screenshotBytes = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.BYTES);
try (
ByteArrayInputStream bytes = new ByteArrayInputStream(screenshotBytes);
ByteArrayOutputStream result = new ByteArrayOutputStream()
) {
// 转换为图片
BufferedImage screenshot = ImageIO.read(bytes);
// 画笔
Graphics2D graphics = screenshot.createGraphics();
// 设置“抗锯齿”的属性
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 画笔颜色
graphics.setColor(Color.RED);
// 画笔粗细
graphics.setStroke(new BasicStroke(4f));
// 绘制矩形标记
graphics.drawRect(x - offsetX, y - offsetY, width, height);
// 输出
ImageIO.write(screenshot, "PNG", result);
byte[] screenshotMarkBytes = result.toByteArray();
screenshotMarks.add(new ScreenshotMark(name, screenshotMarkBytes, (long) screenshotMarkBytes.length));
} catch (IOException e) {
System.err.println(name + " "+e.getMessage());
}
}
if (consumer != null) consumer.accept(Optional.of(screenshotMarks));
}
ScreenshotMark
对象
public class ScreenshotMark {
private String name;
private byte[] bytes;
private Long size;
public ScreenshotMark() {
}
public ScreenshotMark(String name, byte[] bytes, Long size) {
this.name = name;
this.bytes = bytes;
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte[] getBytes() {
return bytes;
}
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
}
测试
public static void main(String[] args) throws IOException {
// 设置驱动路径环境变量
// 必须是全路径
System.setProperty("webdriver.chrome.driver", "<path>/chromedriver.exe");
WebDriver webDriver = null;
try {
ChromeOptions options = initWebOption("");
webDriver = new ChromeDriver(options);
webDriver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
webDriver.manage().window().maximize();
webDriver.get("https://www.baidu.com");
long timeout = 15000;
webDriver.manage().window().setSize(new Dimension(1920, 1080));
webDriver.manage().timeouts().pageLoadTimeout(timeout, TimeUnit.MILLISECONDS);
webDriver.manage().timeouts().setScriptTimeout(timeout, TimeUnit.MILLISECONDS);
webDriver.manage().timeouts().implicitlyWait(timeout, TimeUnit.MILLISECONDS);
System.out.println("打开驱动实例成功...");
// 获取所有的a标签,并标记
List<WebElement> webElements = webDriver.findElements(By.xpath("//a"));
ScreenshotServiceImpl screenshotService = new ScreenshotServiceImpl();
screenshotService.screenshotAndMark(webDriver, webElements, WebElement::getText, optional -> {
// 该处为了演示,实际可以拿到bytes[] 上传到服务器,或其他地方
if (optional.isPresent()) {
for (ScreenshotMark screenshotMark : optional.get()) {
String name = screenshotMark.getName();
name = name == null || name.length() == 0 ? Math.random() + "" : name;
try (ByteArrayInputStream bytes = new ByteArrayInputStream(screenshotMark.getBytes())) {
BufferedImage image = ImageIO.read(bytes);
ImageIO.write(image, "png", new File("<path>/" + name + ".png"));
} catch (IOException e) {
System.err.println("error finally:" + e.getMessage());
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("completed finally.");
if (webDriver != null) webDriver.quit();
}
}
备注
原文链接 IT浪子の博客 > Selenium ChromeDriver 截图标记指定元素的方法
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。