Java/Kotlin selenium 无头浏览器 [Headless Chrome] 实现长截图
一、CDP 截图
CDP 截图,是通过 Chrome 的 Chrome DevTools Protocol (CDP) 协议进行截图。如果需要使用这个,需要保证 浏览器版本,浏览器驱动,项目 selenium 依赖,chrome cdp 的依赖。这些版本需要对应后,才能正常使用。
// 1. 查找 CDP 版本
Optional<CdpVersion> optional = new CdpVersionFinder().match(driver.capabilities.browserVersion);
if (!optional.isPresent()) {throw new RuntimeException("No CDP version found. Please check the browser version to select the correct CDP version");
}// 2. 构造参数
Map<String, Object> params = new HashMap<>();
params.put("format", "png");
params.put("captureBeyondViewport", true);// 3. 执行 CDP 命令
@SuppressWarnings("unchecked")
Map<String, String> response = (Map<String, String>) driver.executeCdpCommand("Page.captureScreenshot", params);// 4. 提取并处理 base64 图像数据
String data = response.get("data");
String base64Image = data.replaceFirst("^data:image/png;base64,", "");
二、修改浏览器大小截图
通过将浏览器的大小修改为页面的大小,这样截图天生就是长截图但是需要注意:如果想要截图需要使用无头浏览器,否则截取的图片大小不会超过视窗窗口的大小的。
public class ScreenshotUtils {/*** 截取整个页面的屏幕截图(包括超出视口的部分)** @param instance TakesScreenshot 实例(WebDriver 或 WebElement)* @param returnType 返回类型(如 OutputType.FILE 或 OutputType.BYTES)* @param <T> 返回值类型* @return 截图结果*/public static <T> T fullScreenshotAsT(TakesScreenshot instance, Class<T> returnType) {WebDriver driver;// 确定 WebDriver 实例if (instance instanceof WebDriver) {driver = (WebDriver) instance;} else if (instance instanceof WebElement) {if (instance instanceof WrapsDriver) {driver = ((WrapsDriver) instance).getWrappedDriver();} else {throw new UnsupportedOperationException("WebElement does not wrap a WebDriver");}} else {throw new UnsupportedOperationException("Unsupported TakesScreenshot type: " + instance.getClass().getName());}// 获取原始窗口大小Dimension originalSize = driver.manage().window().getSize();// 获取整个页面的滚动尺寸Dimension scrollSize = getFullPageSize(driver);// 调整窗口大小以适应整个页面driver.manage().window().setSize(scrollSize);T screenshot = null;try {// 截图screenshot = instance.getScreenshotAs(returnType);} finally {// 恢复窗口大小driver.manage().window().setSize(originalSize);}return screenshot;}/*** 获取整个页面的滚动尺寸(包括超出视口的部分)** @param driver WebDriver 实例* @return 页面的完整尺寸*/private static Dimension getFullPageSize(WebDriver driver) {JavascriptExecutor js = (JavascriptExecutor) driver;Long scrollHeight = (Long) js.executeScript("return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)");Long scrollWidth = (Long) js.executeScript("return Math.max(document.body.scrollWidth, document.documentElement.scrollWidth)");return new Dimension(scrollWidth.intValue(), scrollHeight.intValue());}
}
三、 AShot 截图库
public class ScreenshotUtils {/*** 获取当前页面的 devicePixelRatio(DPR)** @param driver WebDriver 实例* @return devicePixelRatio 值*/public static float getDevicePixelRatio(WebDriver driver) {Object result = ((JavascriptExecutor) driver).executeScript("return window.devicePixelRatio;");if (result instanceof Number) {return ((Number) result).floatValue();}return 1.0f; // 默认值}/*** 使用 AShot 截图策略,返回 BufferedImage** @param instance TakesScreenshot 实例(WebDriver 或 WebElement)* @param <T> 返回值类型(如 BufferedImage)* @return 截图的 BufferedImage*/public static BufferedImage takeScreenshotWithDpr(TakesScreenshot instance) {WebDriver driver;if (instance instanceof WebDriver) {driver = (WebDriver) instance;} else if (instance instanceof WebElement) {if (instance instanceof WrapsDriver) {driver = ((WrapsDriver) instance).getWrappedDriver();} else {throw new UnsupportedOperationException("WebElement does not wrap a WebDriver");}} else {throw new UnsupportedOperationException("Unsupported TakesScreenshot type: " + instance.getClass().getName());}float dpr = getDevicePixelRatio(driver);ShootingStrategy shootingStrategy = ShootingStrategies.viewportRetina(800, 0, 0, dpr);AShot aShot = new AShot().shootingStrategy(shootingStrategy).coordsProvider(new WebDriverCoordsProvider());BufferedImage bufferedImage;if (instance instanceof WebDriver) {bufferedImage = aShot.takeScreenshot(driver).getImage();} else if (instance instanceof WebElement) {WebElement element = (WebElement) instance;bufferedImage = aShot.takeScreenshot(driver, element).getImage();} else {throw new IllegalStateException("Unexpected TakesScreenshot instance");}return bufferedImage;}
}