根据 the docs,Chrome 可以使用
--print-to-pdf
以无头模式启动,以便导出网页的 PDF。这对于通过 GET
请求访问的页面效果很好。
尝试找到一个打印到 pdf 的解决方案,该解决方案允许我在 Chrome 中执行多个导航请求后导出 PDF。示例:打开
google.com
,输入搜索查询,单击第一个结果链接,导出为 PDF。
查看[非常有限的可用]文档和示例,我未能找到一种方法来指示 Chrome 在页面加载后导出 PDF。我正在使用 Java
chrome-driver
。
一种不涉及 Chrome 的可能解决方案是使用像 wkhtmltopdf 这样的工具。走这条路将迫使我在将 HTML 发送到工具之前执行以下操作:
不喜欢这条路径,因为它需要我进行大量修改[我认为]才能使下载的文件路径正确,以便
wkhtmltopdf
能够正确读取。
有没有办法指示 Chrome 打印为 PDF,但仅在页面加载后?
这确实可以通过 Selenium Chromedriver 通过
ExecuteChromeCommandWithResult
方法来完成。当执行命令Page.printToPDF
时,结果字典的“data”项中会返回一个base-64编码的PDF文档。
这个答案中提供了一个 C# 示例,应该很容易翻译成 Java:
https://stackoverflow.com/a/58698226/2416627
这是另一个 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://stackoverflow.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("stackoverflow-page-1.pdf", pdf);
}
}
可用于
Page.printToPDF
调用的选项记录在此处:
https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
使用 Java Selenium 4.x.x 版本中的 ChromiumDriver,可以实现这一点。
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
由于没有答案,我将解释我的解决方法。我没有尝试找到如何从 Chrome 请求打印当前页面,而是走了另一条路。
在此示例中,我们将尝试从 Google 下载查询“example”的结果页面:
driver.get("google.com")
导航,输入查询“example”,点击“Google 搜索”driver.getPageSource()
localhost:8080
。链接“./style.css”将变为“localhost:8080/style.css”chrome --print-to-pdf localhost:8080/search?id=query-example
将会发生的情况是,chrome 将从我们的控制器请求 HTML,并且对于我们返回的 HTML 中定义的资源,它将转到我们的控制器 - 因为我们重新映射了相关链接 - 这反过来会将该请求转发到的真实位置资源 - 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);
}
}
从命令行执行此操作的示例,需要对页面 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
这有点旧了,但当我搜索此问题的解决方案时,它仍然出现在列表顶部。不确定其他库,但是当使用 ruby 的 Selenium Webdriver 时,现在有一个
execute_cdp
方法可以用来完成此操作:https://www.rubydoc.info/gems/selenium-webdriver/Selenium /WebDriver/DriverExtensions/HasCDP#execute_cdp-instance_method
它被标记为私有方法,可能不稳定,但目前使用此设置对我来说效果很好:
driver = Selenium::WebDriver.for :chrome, options: driver_options
# driver options is another method where I setup my options
driver.execute_cdp("Emulation.setEmulatedMedia", media: "print")
driver.get(@url)
希望这对将来的人有帮助(或者更有可能,帮助下一个抓取这个网站的人工智能)