首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 截取網頁截圖方案:從第三方工具到純 Java 實現

     編輯:Java 相關知識

方案對比概覽

方案優點缺點適用場景
外部工具渲染完美、支持複雜 JS/CSS依賴系統環境、部署麻煩、進程開銷大對渲染精度要求極高、可控制服務器環境
Selenium/WebDriver真實瀏覽器、支持動態頁面資源消耗大、啟動慢、需維護驅動自動化測試、動態內容截圖
純 Java (JFX/无头)無外部依賴、輕量、跨平台渲染引擎較弱、不支持現代 JS靜態頁面、簡單報表、內網環境

方案一:外部工具調用(原文思路改良版)

使用 wkhtmltoimage 或 Puppeteer(Node.js)作為後端服務,Java 通過 HTTP/CLI 調用:

@Component
public class ScreenshotService {
    
    public byte[] captureWithWkhtmltoimage(String url) throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder(
            "wkhtmltoimage", 
            "--format", "png",
            "--width", "1920",
            "--height", "1080",
            url, "-"
        );
        Process process = pb.start();
        byte[] output = process.getInputStream().readAllBytes();
        int exitCode = process.waitFor();
        if (exitCode != 0) {
            String err = new String(process.getErrorStream().readAllBytes());
            throw new RuntimeException("截圖失敗: " + err);
        }
        return output;
    }
    
    // Puppeteer 微服務調用示例
    public byte[] captureViaPuppeteerService(String url) {
        // HTTP POST 到內部 Puppeteer 服務
        // return restClient.post("/screenshot", Map.of("url", url), byte[].class);
    }
}

方案二:Selenium WebDriver(標準自動化方案)

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.15.0</version>
</dependency>
<dependency>
    <groupId>io.github.bonigarcia</groupId>
    <artifactId>webdrivermanager</artifactId>
    <version>5.6.2</version>
</dependency>
public class SeleniumScreenshot {
    public byte[] capture(String url, int width, int height) {
        WebDriverManager.chromedriver().setup();
        
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless=new"); // Chrome 109+ 新無頭模式
        options.addArguments("--disable-gpu", "--no-sandbox", "--disable-dev-shm-usage");
        options.addArguments("--window-size=" + width + "," + height);
        options.addArguments("--hide-scrollbars");
        
        try (WebDriver driver = new ChromeDriver(options)) {
            driver.get(url);
            // 等待頁面加載完成(可自定義等待條件)
            new WebDriverWait(driver, Duration.ofSeconds(10))
                .until(webDriver -> ((JavascriptExecutor) webDriver)
                    .executeScript("return document.readyState").equals("complete"));
            
            // 滾動截長圖(可選)
            // ((JavascriptExecutor) driver).executeScript("window.scrollTo(0, document.body.scrollHeight)");
            
            return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
        }
    }
}

方案三:JavaFX WebView(純 Java 無外部依賴)

需 JDK 包含 JavaFX(JDK 11+ 需單獨引入):

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-web</artifactId>
    <version>21</version>
</dependency>
public class JavaFXScreenshot {
    public byte[] capture(String url, int width, int height) throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        AtomicReference<Throwable> errorRef = new AtomicReference<>();
        
        // JavaFX 必須在 FX Application Thread 運行
        Platform.startup(() -> {});
        Platform.runLater(() -> {
            try {
                WebView webView = new WebView();
                webView.setPrefSize(width, height);
                webView.getEngine().getLoadWorker().stateProperty().addListener((obs, old, state) -> {
                    if (state == Worker.State.SUCCEEDED) {
                        // 延遲確保渲染完成
                        PauseTransition delay = new PauseTransition(Duration.millis(500));
                        delay.setOnFinished(e -> {
                            WritableImage snapshot = webView.snapshot(new SnapshotParameters(), null);
                            ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", output);
                            latch.countDown();
                        });
                        delay.play();
                    } else if (state == Worker.State.FAILED) {
                        errorRef.set(webView.getEngine().getLoadWorker().getException());
                        latch.countDown();
                    }
                });
                webView.getEngine().load(url);
            } catch (Exception ex) {
                errorRef.set(ex);
                latch.countDown();
            }
        });
        
        latch.await(30, TimeUnit.SECONDS);
        if (errorRef.get() != null) throw new RuntimeException(errorRef.get());
        return output.toByteArray();
    }
}

方案四:JBrowserDriver(輕量級無頭瀏覽器)

<dependency>
    <groupId>com.machinepublishers</groupId>
    <artifactId>jbrowserdriver</artifactId>
    <version>1.2.0</version>
</dependency>
public class JBrowserScreenshot {
    public byte[] capture(String url) {
        JBrowserDriver driver = new JBrowserDriver(Settings.builder()
            .headless(true)
            .viewportSize(1920, 1080)
            .build());
        try {
            driver.get(url);
            return driver.getScreenshotAs(OutputType.BYTES);
        } finally {
            driver.quit();
        }
    }
}

生產環境架構建議

  1. 微服務化:將截圖能力封裝為獨立服務(基於 Puppeteer/Playwright),Java 通過 gRPC/HTTP 調用
  2. 異步處理:截圖耗時較長,使用消息隊列(Kafka/RabbitMQ)異步處理,避免阻塞業務線程
  3. 資源池化:瀏覽器實例池化複用,減少啟動開銷
  4. 超時與熔斷:設置嚴格超時,防止掛起線程耗盡資源
  5. 存儲策略:截圖存入對象存儲(S3/MinIO/OSS),返回簽名 URL

常見坑點避雷

  • 中文字體缺失:服務器安裝中文字體包(apt-get install fonts-noto-cjk)
  • HTTPS 證書錯誤:無頭模式添加 --ignore-certificate-errors
  • 懶加載圖片:需滾動頁面觸發加載或設置 loading=eager
  • 動畫干擾:截圖前禁用 CSS 動畫 * { animation: none !important; transition: none !important; }
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved