【转】Chrome 的无头模式升级了:推出 --headless=new
Chrome
的无头模式变得更好用了!本文概要介绍了近期的工程工作,让 Headless
更接近Chrome
的常规“Headful”模式,让Headless
对开发者更有用。
背景
早在 2017 年,Chrome 59
便引入了所谓的无头模式,可让您在没有任何可见界面的无人值守环境中运行浏览器。从本质上讲,就是在不使用 Chrome
的情况下运行 Chrome
!
无头模式是一种通过 Puppeteer
或 ChromeDriver
等项目实现浏览器自动化的热门选择。以下是使用无头模式创建指定网址的PDF
文件的极简命令行示例:
chrome --headless --print-to-pdf https://developer.chrome.com/
Headless 有哪些新变化?
在深入了解最新的Headless
改进之前,我们有必要先了解一下旧版 Headless
的工作原理。我们之前展示的命令行代码段使用了 --headless
命令行标记,这表明无头只是常规Chrome
浏览器的一种操作模式。但令人惊讶的是,事实并非如此。从技术上讲,旧版 Headless 是一种独立的替代浏览器实现方案,它正好是作为同一 Chrome
二进制文件的一部分一起提供的。但不共享 chrome
中的任何 Chrome
浏览器代码。
正如您所想象的,实施和维护这个独立的无头浏览器会产生大量的工程开销,但这不是唯一的问题。由于 Headless
是一种单独的实现,因此它有自己的 bug
和功能,而这些 bug
和功能是头部Chrome
中不存在的。这就造成了一种令人困惑的情况:任何自动化浏览器测试在有头模式下都可能会通过,但在无头模式下却会失败,反之亦然。这是自动化工程师的一大痛点。它还排除了任何依赖于安装浏览器扩展程序的自动化测试,例如测试。这同样适用于任何其他浏览器级功能:除非 Headless
有自己的单独实现,否则不受支持。
2021 年,Chrome 团队着手解决此问题,并一次性统一了无头模式和有头模式。
我们很高兴地宣布,Chrome 112
中现已提供新的无头模式!在此模式下,Chrome
会创建但不显示任何平台窗口。所有其他功能(包括现有和未来的功能)均不受限制。
体验全新的无头模式
如需试用新的无头模式,请传递 --headless=new
命令行标志
chrome --headless=new
目前,旧版无头模式仍可通过以下方式使用:
chrome --headless=old
目前,如果在没有明确值的情况下传递 --headless
命令行标志,系统仍会激活旧的无头模式,但我们计划随着时间的推移将此默认设置更改为新的无头模式。
我们计划从 Chrome
二进制文件中彻底移除旧版 Headless
,并于今年晚些时候停止在 Puppeteer
中支持此模式。在此次移除过程中,我们将把旧版 Headless
作为单独的独立二进制文件提供给无法升级的用户。
Puppeteer 推出全新无头模式
如需在 Puppeteer
中选择启用新的无头模式,请执行以下操作:
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch({ headless: 'new', // `headless: true` (default) enables old Headless; // `headless: 'new'` enables new Headless; // `headless: false` enables "headful" mode. }); const page = await browser.newPage(); await page.goto('https://developer.chrome.com/'); // … await browser.close();
Selenium-WebDriver 中的全新无头
如需在 Selenium-WebDriver
中使用新的无头模式,请执行以下操作:
const driver = await env .builder() .setChromeOptions(options.addArguments('--headless=new')) .build(); await driver.get('https://developer.chrome.com/'); // … await driver.quit();
如需了解详情,包括使用其他语言绑定的示例,请参阅 Selenium 团队的博文。
特定于无头的命令行标志
以下命令行 flag 适用于新的无头模式。
--dump-dom
--dump-dom
标志会将目标网页的序列化 DOM
输出到 stdout
。示例如下:
chrome --headless=new --dump-dom https://developer.chrome.com/
请注意,这与简单地输出HTML
源代码(您可以通过 curl
完成此操作)不同。为了生成 --dump-dom
的输出,Chrome
首先会将 HTML
代码解析为 DOM
,执行任何可能会更改 DOM
的 <script>
,然后将该 DOM
重新转换为序列化的 HTML
字符串。
--screenshot
--screenshot
标记会截取目标网页的屏幕截图,并将其作为 screenshot.png
保存到当前工作目录中。与 --window-size
标志结合使用尤为有用。示例如下:
chrome --headless=new --screenshot --window-size=412,892 https://developer.chrome.com/
--print-to-pdf
--print-to-pdf
标志可将目标网页另存为当前工作目录中名为 output.pdf
的 PDF
。示例如下:
chrome --headless=new --print-to-pdf https://developer.chrome.com/
您还可以选择添加 --no-pdf-header-footer
标记,以省略打印页眉(包含当前日期和时间)和页脚(包含网址和页码)。
chrome --headless=new --print-to-pdf --no-pdf-header-footer https://developer.chrome.com/
注意:--no-pdf-header-footer
标志后面的功能之前可通过 --print-to-pdf-no-header
标志使用。根据您使用的 Chrome
版本,您可能需要回退到旧的 flag 名称。
--timeout
--timeout
标志定义了最长等待时间(以毫秒为单位),之后,即使网页仍在加载,--dump-dom
、--screenshot
和 --print-to-pdf
也会捕获该网页的内容。
chrome --headless=new --print-to-pdf --timeout=5000 https://developer.chrome.com/
--timeout=5000
标志会告知 Chrome
最多等待 5 秒再打印 PDF
。因此,此过程最多只需 5 秒即可运行完毕。
--virtual-time-budget
--virtual-time-budget
支持时间旅行!嗯,在某种程度上对于任何具有时效性的代码(例如,setTimeout
/setInterval
),虚拟时间可充当“快进”。它会强制浏览器尽快执行相应网页的任何代码,同时让网页相信时间实际上会经过。
为便于说明其用途,请参考此演示页面,该页面使用 setTimeout(fn, 1000)
每秒递增、记录和显示一个计数器。下面是相关代码:
<output>0</output> <script> const element = document.querySelector('output'); let counter = 0; setInterval(() => { counter++; console.log(counter); element.textContent = counter; }, 1_000); </script>
一秒后,网页会显示“1”;两秒后,会显示“2”,依此类推。下面展示了如何捕获网页在 42 秒后的状态并将其保存为 PDF
格式:
chrome --headless=new --print-to-pdf --virtual-time-budget=42000 https://mathiasbynens.be/demo/time
--allow-chrome-scheme-url
如需访问 chrome://
网址,必须使用 --allow-chrome-scheme-url
标志。从 Chrome 123
开始提供此标志。示例如下:
chrome --headless=new --print-to-pdf --allow-chrome-scheme-url chrome://gpu
调试
由于 Chrome
在无头模式下实际上不可见,因此似乎很难弄清楚在出现问题时出了什么问题。幸运的是,使用Headless Chrome
调试无头 Chrome
的方式与有头 Chrome
非常相似。技巧就是使用 --remote-debugging-port
命令行 flag
以无头模式启动 Chrome
。
chrome --headless=new --remote-debugging-port=0 https://developer.chrome.com/
此命令会将唯一的WebSocket
网址输出到 stdout
,例如:
DevTools listening on ws://127.0.0.1:60926/devtools/browser/b4bd6eaa-b7c8-4319-8212-225097472fd9
在常规的有头 Chrome
实例中,我们随后可以使用 Chrome 开发者工具远程调试连接到无头目标并对其进行检查。为此,请转到 chrome://inspect
,点击 Configure... 按钮,然后输入 WebSocket 网址的 IP 地址和端口号。在上面的示例中,我输入了 127.0.0.1:60926
。点击 Done,您应该会看到远程目标及其所有标签页和其他目标列在下方。点击inspect
,即可使用 Chrome 开发者工具检查远程无头目标,inspect
!