页面加载后的 ChromeDriver --print-to-pdf

ChromeDriver --print-to-pdf after page load

根据 the docs,Chrome 可以使用 --print-to-pdf 以无头模式启动,以便导出网页的 PDF。这适用于可通过 GET 请求访问的页面。

正在尝试找到一个打印到 PDF 的解决方案,使我能够在 Chrome 中执行多个导航请求后导出 PDF。示例:打开 google.com,输入搜索查询,单击第一个结果 link,导出为 PDF。

查看 [可用数量非常有限] 文档和示例,我未能找到指示 Chrome 在页面加载后导出 PDF 的方法。我正在使用 Java chrome-driver.

一种不涉及 Chrome 的可能解决方案是使用像 wkhtmltopdf 这样的工具。走这条路会迫使我 - 在将 HTML 发送到工具之前 - 执行以下操作:

不喜欢此路径,因为它需要我进行大量修改 [我假设] 才能使下载的文件路径正确,以便 wkhtmltopdf 正确读取。

有没有办法指示 Chrome 打印为 PDF,但仅在页面加载后才能打印?

由于没有答案,我将解释我的解决方法。我没有试图找到如何从 Chrome 请求打印当前页面,而是走另一条路。

对于此示例,我们将尝试从查询 'example':

中的 Google 下载结果页面
  1. 使用 driver.get("google.com") 导航,输入查询 'example',单击 'Google Search'
  2. 等待结果页面加载
  3. 使用 driver.getPageSource()
  4. 检索页面源
  5. 解析源代码,例如Jsoup 以便重新映射所有相关链接以指向为此目的定义的端点(在下面解释)- localhost:8080 的示例。 Link './style.css' 会变成 'localhost:8080/style.css'
  6. 保存HTML到一个文件,例如名为 'query-example'
  7. 运行 chrome --print-to-pdf localhost:8080/search?id=query-example

将会发生的是 chrome 将从我们的控制器请求 HTML,对于 HTML 中定义的资源,我们 return,它将转到我们的controller - 因为我们重新映射了相关链接 - 它将反过来将该请求转发到资源的真实位置 - google.com。下面是一个示例 Spring 控制器,请注意该示例不完整,仅供参考。

@RestController
@RequestMapping
public class InternationalOffloadRestController {
  @RequestMapping(method = RequestMethod.GET, value = "/search/html")
  public String getHtml(@RequestParam("id") String id) {
    File file = new File("location of the HTML file", id);
    try (FileInputStream input = new FileInputStream(file)) {
      return IOUtils.toString(input, HTML_ENCODING);
    }
  }
  @RequestMapping("/**") // forward all remapped links to google.com
  public void forward(HttpServletResponse httpServletResponse, ...) {
    URI uri = new URI("https", null, "google.com", -1, 
      request.getRequestURI(), request.getQueryString(), null);
    httpServletResponse.setHeader("Location", uri.toString());
    httpServletResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
  }
}
</pre>

一个从命令行执行此操作的示例,需要对页面 html 和 sed:

进行一些修改
LOGIN='myuserid'
PASSW='mypasswd'
AUTH='pin=$LOGIN&accessCode=$PASSW&Submit=Submit'
TIMESTAMP=`TZ=HST date -d "today" +"%m/%d/%y %I:%M %p HST"`
wget -q --save-cookies cookies.txt --keep-session-cookies \
    --post-data $AUTH \
    https://csea.ehawaii.gov/iwa/index.html
sed -i 's#href="/iwa/css#href="./bin#g' index.html
sed -i 's#src="/iwa/images#src="./bin#g' index.html
wkhtmltopdf -q --print-media-type \
            --header-left "$d" --header-font-size 10 \
            --header-line --header-spacing 10 \
            --footer-left "Page [page] of [toPage]" --footer-font-size 10 \
            --footer-line --footer-spacing 10 \
            --footer-right "$TIMESTAMP" \
            --margin-bottom 20 --margin-left 15 \
            --margin-top 20 --margin-right 15 \
            index.html index.pdf

假设 cookie 有效,登录后可以访问更多可用页面,如下所示:

wget -q --load-cookies cookies.txt https://csea.ehawaii.gov/otherpage.html
wkhtmltopdf <all the options> otherpage.html otherpage.pdf

此外,我之前已将所有 css 和图像转储到本地 bin 目录中,如下所示:

wget -r -A.jpg -A.gif -A.css -nd -Pbin \
    https://csea.ehawaii.gov/iwa/index.html

这确实可以通过 ExecuteChromeCommandWithResult 方法通过 Selenium Chromedriver 来完成。当执行命令Page.printToPDF时,在结果字典的“数据”项中返回一个base-64编码的PDF文档。

这个答案中提供了一个 C# 示例,应该很容易翻译成 Java:

这是另一个 C# 示例,其中说明了一些有用的选项:

public static void Main(string[] args)
{
    var driverOptions = new ChromeOptions();
    // In headless mode, PDF writing is enabled by default (tested with driver major version 85)
    driverOptions.AddArgument("headless");
    using (var driver = new ChromeDriver(driverOptions))
    {
        driver.Navigate().GoToUrl("https://whosebug.com/questions");
        new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(d => d.FindElements(By.CssSelector("#questions")).Count == 1);
        // Output a PDF of the first page in A4 size at 90% scale
        var printOptions = new Dictionary<string, object>
        {
            { "paperWidth", 210 / 25.4 },
            { "paperHeight", 297 / 25.4 },
            { "scale", 0.9 },
            { "pageRanges", "1" }
        };
        var printOutput = driver.ExecuteChromeCommandWithResult("Page.printToPDF", printOptions) as Dictionary<string, object>;
        var pdf = Convert.FromBase64String(printOutput["data"] as string);
        File.WriteAllBytes("Whosebug-page-1.pdf", pdf);
    }
}

可用于 Page.printToPDF 调用的选项记录在此处:

https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF

使用 ChromiumDriver 来自 Java Selenium 4.x.x 版本,这可以实现。

String command = "Page.printToPDF";
Map<String, Object> params = new HashMap<>();
params.put("landscape", false);
Map<String, Object> output = driver.executeCdpCommand(command, params);
try {
    FileOutputStream fileOutputStream = new FileOutputStream("export.pdf");
    byte[] byteArray = Base64.getDecoder().decode((String)output.get("data"));
    fileOutputStream.write(byteArray);
} catch (IOException e) {
    e.printStackTrace();
}

来源:Selenium_CDP