ヘッドレス Chrome シェル

ヘッドレス Chrome シェル は Chrome 59 でリリースされました。これは、ヘッドレス環境で Chrome ブラウザを実行する方法です。 つまり、Chrome なしで Chrome を実行します。 Chromium と Blink レンダリング エンジンが提供する最新のウェブ プラットフォーム機能 をすべてコマンドラインで使用できます。

ヘッドレス ブラウザは、表示される UI シェルを必要としない自動テストやサーバー環境に最適なツールです。たとえば、実際のウェブページに対してテストを実行したり、その PDF を作成したり、ブラウザが URL をレンダリングする方法を確認したりできます。

ヘッドレスの起動(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 の安定版チャンネルを使用しているが、ベータ版を入手できない場合は、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 を stdout に出力します。

    chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/


### Create a PDF

The `--print-to-pdf` flag creates a PDF of the page:

```shell
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 モード(read-eval-print loop)

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

ブラウザ UI なしで Chrome をデバッグする

--remote-debugging-port=9222 を指定して Chrome を実行すると、 DevTools プロトコルが有効になったインスタンスが起動します。このプロトコルは、Chrome と通信してヘッドレス ブラウザ インスタンスを駆動するために使用されます。Sublime、VS Code、Node などのツールでアプリケーションをリモート デバッグする場合にも使用されます。#synergy

ページを表示するブラウザ UI がないため、別のブラウザで http://localhost:9222 に移動して、すべてが正常に動作していることを確認します。検査可能なページのリストが表示されます。クリックすると、ヘッドレスでレンダリングされている内容を確認できます。

DevTools Remote
DevTools リモート デバッグ UI

ここから、通常の DevTools 機能を使用して、ページを検査、デバッグ、調整できます。ヘッドレスをプログラムで使用している場合、このページは、ブラウザと通信するワイヤを通過するすべての未加工の DevTools プロトコル コマンドを確認できる強力なデバッグツールでもあります。

プログラムで を使用する(Node)

操り人形師

Puppeteer は、Chrome チームが開発した Node ライブラリです 。ヘッドレス(またはフル)Chrome を制御する高レベルの API を提供します。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 セクションでは、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 を使用する

Lighthouse は、ウェブアプリの品質をテストするための優れた ツールです。Chrome を起動するための堅牢なモジュールが Lighthouse 内で開発され、スタンドアロンで使用できるようになりました。 The 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 のインスタンスが表示されます。ブラウザ UI は表示されません。ヘッドレスです。

ブラウザを制御するには、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();

})();

古いヘッドレス Chrome を chrome-headless-shell としてダウンロードする

Chrome バージョン 112 以降では、Chrome の新しいヘッドレス モード(--headless=newを使用できます。このモードを使用すると、デベロッパーは、表示される UI なしで、別のバイナリではなく Chrome 自体を無人環境で実行できます。これは、テストや自動化のユースケースに便利です。

古いヘッドレス シェルと新しいヘッドレス モードには、それぞれ異なるユースケースがあります。

  1. 古いヘッドレス シェルは、Chromium の //content モジュールをラップする軽量ラッパーであるため、依存関係が大幅に少なくなります。具体的には、X11/Wayland、D-Bus を必要とせず、完全な Chrome ブラウザよりもパフォーマンスが優れている場合があります。そのため、自動スクリーンショットやウェブ スクレイピングなどのユースケースに適しています。
  2. 一方、新しいヘッドレス モードは実際の Chrome ブラウザであるため、より信頼性が高く、より多くの機能を提供します。そのため、高精度のエンドツーエンドのウェブアプリ テストやブラウザ拡張機能のテストに適しています。

つまり、パフォーマンスと信頼性の間にはトレードオフがあります。どのヘッドレス モードが最適ですか?ユースケースによって異なります。

上のリストで説明した情報を示す図。

自動化のユースケースで Chrome の完全な機能を必要としないデベロッパーやテスターは、古いヘッドレスを使用することをおすすめします。それ以外の場合は、新しいヘッドレスが最適な選択肢となります。

デベロッパーとテスターが引き続きこの 2 つのオプションを選択できるように、古いヘッドレス実装がスタンドアロンの chrome-headless-shell バイナリとして利用可能になりました。これらの新しい chrome-headless-shell バイナリは、ユーザー向けの Chrome リリースごとに生成され、Chrome 120 以降では Chrome for Testing インフラストラクチャ からダウンロードできます。

chrome-headless-shell バイナリを入手するにはどうすればよいですか?

他の Chrome for Testing バイナリと同様に、プラットフォーム用の chrome-headless-shell をダウンロードする最も簡単な方法は、当社の @puppeteer/browsers コマンドライン ユーティリティ を使用することです。これは npm を使用して利用できます。次に例を示します。

# Download the latest available `chrome-headless-shell` binary corresponding to the Stable channel.
npx @puppeteer/browsers install chrome-headless-shell@stable

# Download a specific `chrome-headless-shell` version.
npx @puppeteer/browsers install chrome-headless-shell@120.0.6098.0

chrome-headless-shell バイナリをダウンロードするための独自の自動スクリプトを作成する場合は、Chrome for Testing を使用できます。Chrome for Testing は、Chrome リリース チャンネル(Stable、Beta、Dev、Canary)ごとに最新の利用可能なバージョンを提供する JSON API エンドポイントを提供します。最新のステータスの概要については、Chrome for Testing の可用性ダッシュボードをご覧ください。

フィードバック

chrome-headless-shell に関するフィードバックをお待ちしております。問題が発生した場合は、ご自由にご報告ください

その他のリソース

以下に、参考になるリソースをご紹介します。

ドキュメント

ツール

  • chrome-remote-interface - Node DevTools プロトコルをラップするモジュール
  • Lighthouse - ウェブアプリの品質をテストするための自動ツール 。プロトコルを多用します。
  • chrome-launcher - 自動化に対応した Chrome を起動するための Node モジュール

デモ

  • The Headless Web」 - Paul Kinlan による、api.ai でヘッドレスを使用する方法に関する優れたブログ 投稿。

よくある質問

--disable-gpu フラグは必要ですか?

Windows のみ。他のプラットフォームでは不要になりました。--disable-gpu フラグは、いくつかのバグに対する一時的な回避策です。Chrome の今後のバージョンでは、このフラグは不要になります。詳しくは、crbug.com/737678 をご覧ください。

Xvfb はまだ必要ですか?

いいえ。ヘッドレス Chrome はウィンドウを使用しないため、Xvfb などのディスプレイ サーバーは不要になりました。自動テストは問題なく実行できます。

Xvfb とはXvfb は、Unix 系のシステム用のインメモリ ディスプレイ サーバーで、物理ディスプレイを接続せずにグラフィカル アプリケーション(Chrome など)を実行できます。 多くのユーザーは、Xvfb を使用して以前のバージョンの Chrome を実行し、「ヘッドレス」テストを行っています。

ヘッドレス Chrome を実行する Docker コンテナを作成するにはどうすればよいですか?

lighthouse-ci をご覧ください。 例の Dockerfile があり、node:8-slim をベースイメージとして使用し、App Engine フレックスに Lighthouse をインストールして 実行します

Selenium / WebDriver / ChromeDriver で使用できますか?

はい。Selenium、WebDriver、ChromeDriver を使用するをご覧ください。

PhantomJS との関係を教えてください。

ヘッドレス Chrome は PhantomJS などのツールに似ています。どちらもヘッドレス環境での自動テストに使用できます。2 つの主な違いは、Phantom がレンダリング エンジンとして古いバージョンの WebKit を使用するのに対し、ヘッドレス Chrome は最新バージョンの Blink を使用することです。

現時点では、Phantom は DevTools プロトコルよりも高レベルの API も提供しています。

バグを報告するにはどうすればよいですか?

ヘッドレス Chrome のバグについては、crbug.com で報告してください。

DevTools プロトコルのバグについては、github.com/ChromeDevTools/devtools-protocol で報告してください。