Chrome 的无头模式升级了:推出 --headless=new

Chrome 的无头模式变得更好用了!

Mathias Bynens
Mathias Bynens
Peter Kvitek
Peter Kvitek

Chrome 的无头模式变得更好用了!本文概要介绍了近期的工程工作,让 Headless 更接近 Chrome 的常规“Headful”模式,让 Headless 对开发者更有用。

背景

早在 2017 年,Chrome 59 便引入了所谓的无头模式,可让您在没有任何可见界面的无人值守环境中运行浏览器。从本质上讲,就是在不使用 Chrome 的情况下运行 Chrome!

无头模式是一种通过 PuppeteerChromeDriver 等项目实现浏览器自动化的热门选择。以下是使用无头模式创建指定网址的 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 Headless 不再是一个单独的浏览器实现方案,现在改为与 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/

--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

Chrome 开发者工具可以检查远程无头目标网页

反馈

我们期待收到您对新的无头模式的反馈。如果您遇到任何问题,请报告