无头 Chrome 使用入门

要点

无头 Chrome 在 Chrome 59 中提供。它是一种在无头环境中运行 Chrome 浏览器的方法。 从本质上讲, 已不使用 Chrome 的 Chrome!它融合了 Google 提供的所有现代网络平台功能

这有何用处?

无头浏览器非常适合自动化测试和服务器环境 不需要可见的界面 shell。例如,您可能想针对 或仅检查浏览器渲染网址的方式。

启动无头 (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 版。

命令行功能

在某些情况下,您可能不需要以编程方式编写 Headless Chrome 的脚本。 有一些实用的命令行 flag 来执行常见任务。

输出 DOM

--dump-dom 标志会将 document.body.innerHTML 输出到 stdout:

    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 表达式求值的模式下运行 Headless 在浏览器中直接运行以下命令:

$ 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 在另一个浏览器中,检查是否一切正常。您会看到 您可以点击这些可检查页面来查看无头设备正在呈现什么内容:

<ph type="x-smartling-placeholder">
</ph> 开发者工具远程 <ph type="x-smartling-placeholder">
</ph> 开发者工具远程调试界面

在这里,您可以使用熟悉的开发者工具功能检查、调试和调整 与往常一样访问该网页如果您是以编程方式使用无头设备,则 页面也是一个强大的调试工具,可用于查看所有原始开发者工具协议 从而与浏览器通信

以编程方式使用 (Node)

木偶操作师

Puppeteer 是一个 Node 库 由 Chrome 团队开发它提供了一个用于控制无头的高级别 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();
})();

请参阅 Puppeteer 的文档 ,详细了解完整 API。

CRI 库

chrome-remote-interface 这个库级别低于 Puppeteer 的 API。如果您想 直接使用 DevTools 协议

正在启动 Chrome

chrome-remote-interface 无法为您启动 Chrome 你会自己照顾好它

在 CLI 部分中,我们使用以下命令手动启动 Chrome--headless --remote-debugging-port=9222。不过,要完全自动化测试 想从您的应用生成 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

灯塔是一座 用于测试 Web 应用质量的工具用于启动应用的强大模块 Chrome 是在 Lighthouse 内开发的,现在提取供独立使用。 chrome-launcher NPM 模块 会找到 Chrome 已安装完毕,请设置调试实例,启动浏览器并终止它 在程序完成后运行最棒的一点是,它可以跨平台运行,这得益于 节点!

默认情况下,chrome-launcher 会尝试启动 Chrome Canary 版(如果它 但您可以更改此设置,手动选择要使用的 Chrome。接收者 则需要先从 npm 安装:

npm i --save chrome-launcher

示例 - 使用 chrome-launcher 启动 Headless

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();
});

运行此脚本不会产生什么效果,但您应该会看到 Chrome 会在加载 about:blank 的任务管理器中启动。请注意 不会是任何浏览器界面我们是无头人。

要控制浏览器,我们需要开发者工具协议!

检索有关网页的信息

我们来安装该库:

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

示例 - 检查网站是否有 Web 应用清单

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 的完整实例。也就是说, 自动化解决方案,但并非完全无头。然而,当人们使用 只需稍加工作量即可运行无头 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();

})();

更多资源

以下是一些可帮助您上手的实用资源:

文档

工具

  • chrome-remote-interface - 节点 用于封装开发者工具协议的模块
  • Lighthouse - 用于测试的自动化工具 Web 应用质量;大量使用协议
  • chrome-launcher: 用于启动 Chrome 的节点模块,已准备好实现自动化

演示

  • The Headless Web”- Paul Kinlan 的精彩博客 有关如何将 Headless 与 api.ai 结合使用的博文。

常见问题解答

我需要 --disable-gpu 标志吗?

仅适用于 Windows。其他平台不再需要。--disable-gpu 标志是一个 可以针对一些错误提供临时解决方法在 Chrome。请访问 crbug.com/737678

所以我仍然需要 Xvfb?

否。无头 Chrome 不使用窗口,因此 Xvfb 等显示服务器 。您可以放心地在没有它的情况下运行自动化测试。

什么是 Xvfb?Xvfb 是一种内存中显示服务器,适用于类 Unix 系统,让您可以 在没有连接物理显示器的情况下运行图形应用(如 Chrome)。 很多人使用 Xvfb 运行 Chrome 的早期版本测试。

如何创建运行 Headless Chrome 的 Docker 容器?

请查看 lighthouse-ci。它具有 Dockerfile 示例 使用 node:8-slim 作为基础映像,安装 + 运行 Lighthouse App Engine Flex 上的

我可以将该功能与 Selenium / WebDriver / ChromeDriver 结合使用吗?

可以。请参阅使用 Selenium、WebDriver 和 ChromeDriver

这与 PhantomJS 有什么关系?

无头 Chrome 与 PhantomJS 等工具类似。两者都有 可在无头环境中用于自动化测试。主要区别在于 两者的区别在于,Phantom 在渲染时使用旧版 WebKit, 而 Headless Chrome 会使用最新版 Blink。

目前,Pantom 提供的 API 级别高于 DevTools 协议

在哪里报告 bug?

对于与无头 Chrome 相关的错误,请在 crbug.com 上提交。

对于开发者工具协议中的错误,请在 github.com/ChromeDevTools/devtools-protocol 报告错误。