要点
无头 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
标志会以无头模式运行,您可以在命令行中直接在浏览器中评估 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 文件、浏览网页以及提取这些网页的相关信息。如果您想快速自动执行浏览器测试,建议您使用该库。它隐藏了 DevTools 协议的复杂性,并负责处理启动 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 来进行“无头”测试。
如何创建运行无头 Chrome 的 Docker 容器?
请查看 lighthouse-ci。该示例包含一个 Dockerfile 示例,该示例使用 node:8-slim
作为基础映像,在 App Engine Flex 上安装并运行 Lighthouse。
我可以将此 API 与 Selenium / WebDriver / ChromeDriver 搭配使用吗?
可以。请参阅使用 Selenium、WebDriver 和 ChromeDriver。
这与 PhantomJS 有何关系?
无头 Chrome 类似于 PhantomJS 等工具。这两种方式都可以用于在无头环境中进行自动化测试。这两者之间的主要区别在于,Phantom 使用较低版本的 WebKit 作为其渲染引擎,而无头 Chrome 使用最新版本的 Blink。
目前,Phantom 还提供比 DevTools 协议更高级别的 API。
在哪里可以报告 bug?
如需报告与无头 Chrome 相关的 bug,请访问 crbug.com。
如需报告 DevTools 协议中的 bug,请访问 github.com/ChromeDevTools/devtools-protocol 进行报告。