要点
无头 Chrome 将在 Chrome 59 中发布。这是在无头环境中运行 Chrome 浏览器的一种方式。本质上,就是在不使用 Chrome 的情况下运行 Chrome!它可将 Chromium 和 Blink 渲染引擎提供的所有现代网络平台功能引入到命令行中。
这有何用处?
无头浏览器非常适合自动化测试和服务器环境,在这些环境中,您不需要可见的界面 Shell。例如,您可能需要针对真实网页运行一些测试、创建该网页的 PDF 副本,或者仅检查浏览器如何呈现网址。
启动无头 (CLI)
若要开始使用无头模式,最简单的方法是从命令行打开 Chrome 二进制文件。如果您已安装 Chrome 59 或更高版本,请使用 --headless
标志启动 Chrome:
chrome \
--headless \ # Runs Chrome in headless mode.
--disable-gpu \ # Temporarily needed if running on Windows.
--remote-debugging-port=9222 \
https://www.chromestatus.com # URL to open. Defaults to about:blank.
chrome
应指向您安装的 Chrome。确切位置因平台而异。因为我是 Mac 用户,所以我为已安装的每个 Chrome 版本
创建了方便的别名
如果您使用的是 Chrome 的稳定渠道,并且无法获取 Beta 版,建议您使用 chrome-canary
:
alias chrome="/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
alias chrome-canary="/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary"
alias chromium="/Applications/Chromium.app/Contents/MacOS/Chromium"
请点击此处下载 Chrome Canary 版。
命令行功能
在某些情况下,您可能无需以编程方式编写脚本来使用无头 Chrome。 有一些实用的命令行标志可用于执行常规任务。
输出 DOM
--dump-dom
标志会将 document.body.innerHTML
输出到标准输出:
chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/
创建 PDF
--print-to-pdf
标志会创建该页面的 PDF 文件:
chrome --headless --disable-gpu --print-to-pdf https://www.chromestatus.com/
截取屏幕截图
如需截取网页的屏幕截图,请使用 --screenshot
标志:
chrome --headless --disable-gpu --screenshot https://www.chromestatus.com/
# Size of a standard letterhead.
chrome --headless --disable-gpu --screenshot --window-size=1280,1696 https://www.chromestatus.com/
# Nexus 5x
chrome --headless --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/
使用 --screenshot
运行时,系统会在当前工作目录中生成一个名为 screenshot.png
的文件。如果您要获取整页屏幕截图,则需要多做一些工作。David Schnurr 撰写了一篇非常实用的博文,可供您参考。请参阅将无头 Chrome 用作自动截图工具 。
REPL 模式(读取-求值-输出循环)
--repl
标志在一种模式下运行 Headless,您可以在浏览器中直接从命令行对 JS 表达式求值:
$ chrome --headless --disable-gpu --repl --crash-dumps-dir=./tmp https://www.chromestatus.com/
[0608/112805.245285:INFO:headless_shell.cc(278)] Type a Javascript expression to evaluate or "quit" to exit.
>>> location.href
{"result":{"type":"string","value":"https://www.chromestatus.com/features"}}
>>> quit
$
在没有浏览器界面的情况下调试 Chrome?
使用 --remote-debugging-port=9222
运行 Chrome 时,它会启动一个启用了 DevTools 协议的实例。该协议用于与 Chrome 通信并驱动无头浏览器实例。Sublime、VS Code 和 Node 等工具也使用该协议来远程调试应用。#synergy
由于您没有查看该页面的浏览器界面,因此请在另一个浏览器中转到 http://localhost:9222
,以检查是否一切正常。您会看到一个可检查网页的列表,您可以点击这些网页,查看 Headless 正在呈现的内容:
然后,您可以使用熟悉的 DevTools 功能像往常一样检查、调试和调整网页。如果您以编程方式使用无头模式,此页面还可用作强大的调试工具,用于查看通过网络传输并与浏览器通信的所有原始 DevTools 协议命令。
以编程方式使用 (Node)
木偶操作师
Puppeteer 是由 Chrome 团队开发的 Node 库。它提供了高级 API 来控制无头(或完整)Chrome。它与 Phantom 和 NightmareJS 等其他自动化测试库类似,但仅适用于最新版本的 Chrome。
除此之外,Puppeteer 还可用于轻松截取屏幕截图、创建 PDF 文件、浏览网页以及提取这些网页的相关信息。如果您想快速自动执行浏览器测试,建议您使用该库。它隐藏了开发者工具协议的复杂性,并可以处理多余的任务,例如启动 Chrome 的调试实例。
安装方法:
npm i --save puppeteer
示例 - 输出用户代理
const puppeteer = require('puppeteer');
(async() => {
const browser = await puppeteer.launch();
console.log(await browser.version());
await browser.close();
})();
示例 - 截取页面的屏幕截图
const puppeteer = require('puppeteer');
(async() => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.chromestatus.com', {waitUntil: 'networkidle2'});
await page.pdf({path: 'page.pdf', format: 'A4'});
await browser.close();
})();
如需详细了解完整的 API,请参阅 Puppeteer 文档。
CRI 库
chrome-remote-interface 比 Puppeteer 的 API 更低级。如果您希望深入了解底层并直接使用 DevTools 协议,我建议您使用此方法。
启动 Chrome
chrome-remote-interface 不会为您启动 Chrome,因此您必须自行处理。
在 CLI 部分,我们使用 --headless --remote-debugging-port=9222
手动启动了 Chrome。不过,如需完全自动执行测试,您可能需要从应用中生成 Chrome。
一种方法是使用 child_process
:
const execFile = require('child_process').execFile;
function launchHeadlessChrome(url, callback) {
// Assuming MacOSx.
const CHROME = '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome';
execFile(CHROME, ['--headless', '--disable-gpu', '--remote-debugging-port=9222', url], callback);
}
launchHeadlessChrome('https://www.chromestatus.com', (err, stdout, stderr) => {
...
});
但是,如果您想要一个可在多个平台上运行的可移植解决方案,情况就会变得棘手。请看一下 Chrome 的硬编码路径 :(
使用 ChromeLauncher
Lighthouse 是一款出色的网络应用质量测试工具。我们在 Lighthouse 中开发了一个用于启动 Chrome 的强大模块,现在已将其提取出来供独立使用。chrome-launcher
NPM 模块将查找 Chrome 的安装位置、设置调试实例、启动浏览器,并在程序完成后终止它。最棒的是,得益于 Node,它可以跨平台运行!
默认情况下,chrome-launcher
将尝试启动 Chrome Canary 版(如果已安装),但您可以将其更改为手动选择要使用的 Chrome。如需使用该模块,请先通过 npm 进行安装:
npm i --save chrome-launcher
示例 - 使用 chrome-launcher
启动无头
const chromeLauncher = require('chrome-launcher');
// Optional: set logging level of launcher to see its output.
// Install it using: npm i --save lighthouse-logger
// const log = require('lighthouse-logger');
// log.setLevel('info');
/**
* Launches a debugging instance of Chrome.
* @param {boolean=} headless True (default) launches Chrome in headless mode.
* False launches a full version of Chrome.
* @return {Promise<ChromeLauncher>}
*/
function launchChrome(headless=true) {
return chromeLauncher.launch({
// port: 9222, // Uncomment to force a specific port of your choice.
chromeFlags: [
'--window-size=412,732',
'--disable-gpu',
headless ? '--headless' : ''
]
});
}
launchChrome().then(chrome => {
console.log(`Chrome debuggable on port: ${chrome.port}`);
...
// chrome.kill();
});
运行此脚本不会执行太多操作,但您应该会在任务管理器中看到一个已加载 about:blank
的 Chrome 实例启动。请注意,该界面不会提供任何浏览器界面我们是无头的。
如需控制浏览器,我们需要使用 DevTools 协议!
检索网页的相关信息
我们来安装该库:
npm i --save chrome-remote-interface
示例
示例 - 输出用户代理
const CDP = require('chrome-remote-interface');
...
launchChrome().then(async chrome => {
const version = await CDP.Version({port: chrome.port});
console.log(version['User-Agent']);
});
产生类似如下的结果:HeadlessChrome/60.0.3082.0
示例:检查网站是否包含网络应用清单
const CDP = require('chrome-remote-interface');
...
(async function() {
const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});
// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page} = protocol;
await Page.enable();
Page.navigate({url: 'https://www.chromestatus.com/'});
// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
const manifest = await Page.getAppManifest();
if (manifest.url) {
console.log('Manifest: ' + manifest.url);
console.log(manifest.data);
} else {
console.log('Site has no app manifest');
}
protocol.close();
chrome.kill(); // Kill Chrome.
});
})();
示例 - 使用 DOM API 提取网页的 <title>
。
const CDP = require('chrome-remote-interface');
...
(async function() {
const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});
// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page, Runtime} = protocol;
await Promise.all([Page.enable(), Runtime.enable()]);
Page.navigate({url: 'https://www.chromestatus.com/'});
// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
const js = "document.querySelector('title').textContent";
// Evaluate the JS expression in the page.
const result = await Runtime.evaluate({expression: js});
console.log('Title of page: ' + result.result.value);
protocol.close();
chrome.kill(); // Kill Chrome.
});
})();
使用 Selenium、WebDriver 和 ChromeDriver
目前,Selenium 会打开完整的 Chrome 实例。换句话说,这是一种自动化解决方案,但并非完全无头。不过,Selenium 可以配置为通过一些简单的工作来运行无头 Chrome。如果您想详细了解如何自行进行设置,建议您参阅通过无头 Chrome 运行 Selenium。不过,我已在下方添加了一些示例,以便您快速上手。
使用 ChromeDriver
ChromeDriver 2.32 使用 Chrome 61,可与无头 Chrome 搭配使用。
安装:
npm i --save-dev selenium-webdriver chromedriver
示例:
const fs = require('fs');
const webdriver = require('selenium-webdriver');
const chromedriver = require('chromedriver');
const chromeCapabilities = webdriver.Capabilities.chrome();
chromeCapabilities.set('chromeOptions', {args: ['--headless']});
const driver = new webdriver.Builder()
.forBrowser('chrome')
.withCapabilities(chromeCapabilities)
.build();
// Navigate to google.com, enter a search.
driver.get('https://www.google.com/');
driver.findElement({name: 'q'}).sendKeys('webdriver');
driver.findElement({name: 'btnG'}).click();
driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);
// Take screenshot of results page. Save to disk.
driver.takeScreenshot().then(base64png => {
fs.writeFileSync('screenshot.png', new Buffer(base64png, 'base64'));
});
driver.quit();
使用 WebDriverIO
WebDriverIO 是 Selenium WebDriver 之上的更高级别 API。
安装:
npm i --save-dev webdriverio chromedriver
示例:在 chromestatus.com 上过滤 CSS 功能
const webdriverio = require('webdriverio');
const chromedriver = require('chromedriver');
const PORT = 9515;
chromedriver.start([
'--url-base=wd/hub',
`--port=${PORT}`,
'--verbose'
]);
(async () => {
const opts = {
port: PORT,
desiredCapabilities: {
browserName: 'chrome',
chromeOptions: {args: ['--headless']}
}
};
const browser = webdriverio.remote(opts).init();
await browser.url('https://www.chromestatus.com/features');
const title = await browser.getTitle();
console.log(`Title: ${title}`);
await browser.waitForText('.num-features', 3000);
let numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} total features`);
await browser.setValue('input[type="search"]', 'CSS');
console.log('Filtering features...');
await browser.pause(1000);
numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} CSS features`);
const buffer = await browser.saveScreenshot('screenshot.png');
console.log('Saved screenshot...');
chromedriver.stop();
browser.end();
})();
更多资源
以下是一些实用资源,可帮助您上手使用:
文档
- DevTools Protocol Viewer - API 参考文档
工具
- chrome-remote-interface - 封装 DevTools 协议的 Node 模块
- Lighthouse:用于测试 Web 应用质量的自动化工具;大量使用该协议
- chrome-launcher - 用于启动 Chrome 的 Node 模块,可用于自动化操作
演示
- “The Headless Web”(无头 Web)- Paul Kinlan 撰写的一篇出色的博文,介绍了如何将 Headless 与 api.ai 搭配使用。
常见问题解答
我需要 --disable-gpu
标志吗?
仅限 Windows。其他平台不再需要。--disable-gpu
标志是一些 bug 的临时解决方法。在未来的 Chrome 版本中,您就不需要此标志。如需了解详情,请访问 crbug.com/737678。
所以我仍然需要 Xvfb?
不需要。无头 Chrome 不使用窗口,因此不再需要像 Xvfb 这样的显示服务器。您无需它即可顺利运行自动化测试。
什么是 Xvfb?Xvfb 是适用于类 Unix 系统的内存显示服务器,可让您在未连接实体显示屏的情况下运行图形应用(例如 Chrome)。许多人使用 Xvfb 运行较低版本的 Chrome 来进行“无头”测试。
如何创建运行 Headless Chrome 的 Docker 容器?
请查看 lighthouse-ci。该示例包含一个 Dockerfile 示例,该示例使用 node:8-slim
作为基础映像,在 App Engine Flex 上安装并运行 Lighthouse。
我可以将此 API 与 Selenium / WebDriver / ChromeDriver 搭配使用吗?
可以。请参阅使用 Selenium、WebDriver 和 ChromeDriver。
这与 PhantomJS 有什么关系?
无头 Chrome 类似于 PhantomJS 等工具。这两种方式都可以用于在无头环境中进行自动化测试。两者的主要区别在于 Phantom 使用旧版 WebKit 作为渲染引擎,而 Headless Chrome 使用最新版本的 Blink。
目前,Phantom 还提供比 DevTools 协议更高级别的 API。
在哪里可以报告 bug?
如需报告与无头 Chrome 相关的 bug,请访问 crbug.com。
如需报告 DevTools 协议中的 bug,请访问 github.com/ChromeDevTools/devtools-protocol 进行报告。