[速習] Puppeteer ~ヘッドレスChromeでテスト&スクレイピング

第1章Puppeteerの魅力は何か ~ヘッドレスChromeを自由自在に操る

2018年6月2日、PhantomJSのリポジトリがついにアーカイブ化されました。PhantomJSのメインメンテナーは、⁠より高速かつ安定して動作するヘッドレスChromeが登場したことで、PhantomJSの開発を続ける必要がなくなった」述べています

PhantomJSは、TwitterやNetflixでも単体テストやパフォーマンステストで使用されていた、ヘッドレスブラウザのデファクトスタンダードでした。ヘッドレスブラウザとは、GUIGraphical UserInterfaceを必要としないブラウザです。

これまでPhantomJSを使ってきた人たちは、徐々にヘッドレスChromeに切り替え始めています。一方で、⁠切り替えにどのような作業が必要になるのか」「いったい何が変わるのか」がわからないといった声を耳にすることもよくあります。

そこで本特集では、ヘッドレスChromeと、それを扱うためにChromeデベロッパーツールのチームが開発した、Puppeteerの実践的な解説を交えながら、これらの疑問に答えたいと思います。

本特集の構成

第1章では、ヘッドレスChromeとPuppeteerを紹介しながら、PhantomJSやSeleniumといった、これまでのツールとの共通点や違いについて解説します。第2章では、実際のコーディングにあたって必要となる、最新のJavaScriptやPuppeteerのAPIについて解説を進めます。第3章および第4章では、E2Eend to endテストや実在するサービスのスクレイピングなど、具体的な実装例を紹介していきます。

なお本特集には、多数の書籍や雑誌記事を執筆されている大竹智也さんに、レビュアーとして参加していただきました。たいへん貴重なご意見をいただき、心から感謝しています。

サンプルコードでPuppeteerを体験

Puppeteerは、ヘッドレスChromeの操作に特化したNode.js製のライブラリです。Puppeteerには「操り人形師」のような意味があり、英語の発音としては「パペティア」が比較的近いようです。

Puppeteerを使うことで、ヘッドレスChromeの提供する機能が、JavaScriptのプログラムから簡単に操作できるようになりました。これによって、テストの自動化はもちろんのこと、クローリング、スクレイピング、サービス監視およびWebページのPDF化など、さまざまな用途に使用できます。

実行環境

最新のPuppeteer(1.11.0)は、Node.jsのバージョンとして6.4.0以上をサポートしています。本特集に掲載されているコードは、下記のバージョンで動作することを確かめています。

  • Node.js:10.15.0
  • Puppeteer:1.11.0

なお、Puppeteerのサンプルコードでも使用されているasync/await関数は、Node.jsのバージョン7.6.0以上でしか動作しません。

最新の構文が使用できない環境では、Puppeteerのコードはすぐに複雑化してしまう傾向にあります。そのため、Node.jsはできるだけ最新のバージョンを使用するよう心がけましょう。

セットアップ

手もとの環境にNode.jsさえインストールされていれば、Puppeteerをすぐに利用できます。適当なディレクトリを作成し、そのディレクトリ内で下記のコマンドを実行するだけで、セットアップが完了します。

端末1 パッケージ管理を開始する
$ npm init -y
端末2 Puppeteerをパッケージにインストール
$ npm install --save puppeteer@1.11.0

スクリーンショットの撮影

それでは、実際にPuppeteerのサンプルコードを動かしてみましょう。この時点ではまだ、Puppeteerがどのようなツールかは理解していなくても問題ありません。まずは、先ほど作成したディレクトリ内に、⁠example.js」というファイル名で下記のコードを保存してください。

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'screenshot.png'});
  await browser.close();
})();

次に、下記のコマンドでサンプルコードを実行してみましょう。

端末3 サンプルコードを実行する
$ node example.js

この10行に満たないコードを実行するだけで、ヘッドレスChromeを立ち上げ、指定したURLのスクリーンショットを撮影し、プロセスを適切に終了させることができました図1⁠。

図1 保存されたスクリーンショット
図

なお、最新のJavaScriptを使ってコーディングした経験がなければ、サンプルコード内の見慣れない構文に戸惑ってしまうかもしれません。これらの新しい構文のうち、const宣言アロー関数は比較的簡単に理解できます。

しかし、非同期処理を便利に扱うためのasync/await関数は初学者がつまずきやすいと考えられるため、第2章において詳しく解説します。

ヘッドレスブラウザが求められる背景

通常のブラウザ(ヘッド「フル」ブラウザ)操作では、マウス、トラックパッド、キーボードなどの操作を通じて、視覚的に結果を受け取ることが一般的でしょう。それに対して、ヘッドレスブラウザはCLICommand Line Interfaceやプログラムだけを通じて操作を受け付け、視覚的なフィードバックはありません図2⁠。

図2 ヘッドフルブラウザとヘッドレスブラウザ
図2

ヘッドレスブラウザを選択する最大のメリットは、その安定性とパフォーマンスです。GUIGraphicalUser Interfaceを必要としないからこそ、起動が早く、安定して高速に動作させることができます。

ヘッドレスブラウザが必要とされる理由の一つとして、AngularJSReactVue.jsなどのフレームワークを用いた、SPASingle Page Applicationの普及が挙げられます。SPAが登場する前は、JavaScriptは表示を補助する役割でしかありませんでした。そのため、ブラウザ操作を自動化しなくても、サーバによってレンダリングされるHTMLを解析するだけで十分でした。

しかし、これらのSPAでは、実際にブラウザ上でJavaScriptを実行しなければ、最終的にどのようなHTMLがレンダリングされるのかを知ることが難しくなってきました。したがって、このようなサイトにE2Eテストやクローリング、スクレイピングを行うためには、ブラウザによる操作を自動化する必要性が出てきました。

継続的インテグレーションの普及

特に最近では、JenkinsTravis CICircleCIなどの継続的インテグレーション、すなわちCIContinuous Integration環境が構築されることが増えてきました。これらの環境では、最新のソースコードがpushされたり、マージされるたびにサーバ上で自動的にテストが実行されるのが一般的です。

サーバ上でE2Eテストを実行するためには、GUIは必要ありません。そのため、安定して高速に動作するヘッドレスブラウザに対する期待が高まってきました。

ヘッドレスChromeの特徴

ヘッドレスブラウザへの期待の高まりを受け、数年前から噂されていたヘッドレスモードがChrome59から搭載され、利用可能になりました。

これまで本特集では、一貫して「ヘッドレスChrome」という名称を用いてきました。しかし、通常とは異なるChromeのバージョンが特別に存在するわけではなく、ヘッドレスモードで起動したChromeのことを便宜上「ヘッドレスChrome」と呼んでいます。

PhantomJSとの違い

PhantomJSとヘッドレスChromeは、どちらもヘッドレスで実行される点でとてもよく似ています。大きな違いとして、PhantomJSがレンダリングエンジンに古いWebKitを使用しているのに対して、ヘッドレスChromeは最新のBlinkを採用していることです表1⁠。

表1 PhantomJSとヘッドレスChromeの比較
PhantomJS ヘッドレスChrome
開発状況 終了 活発
レンダリングエンジン 古いWebKit 最新のBlink
パフォーマンス
標準仕様への対応
ヘッド「フル」モード ×
複数タブ対応 ×

ヘッドレスChromeに切り替えることで、高いパフォーマンスの恩恵を受け、さらに世界で半数のシェアを持つChromeと高い互換性を維持できます。ただし、どうしてもWebKitを使い続けたい場合は、開発の終了したPhantomJSか、ヘッドレスブラウザではありませんが、SafariのようなWebKitベースのブラウザを自動化する必要があります。

パフォーマンス

Hacker Noonの記事で、ヘッドレスChromeとPhantomJSのパフォーマンスを比較した結果が紹介されています。検証方法は、それぞれのヘッドレスブラウザを用いて、Ruby on Railsのデフォルトページに1,000回アクセスするだけの単純なものです。

ベンチマークの結果、ヘッドレス ChromeはPhantomJSと比較して、45%の時間のうちに、62%のメモリ消費量で動作するという結果を残し、高いパフォーマンスが発揮できることを証明しました。

標準仕様への対応

ヘッドレスChromeを採用する大きなメリットの一つとして、最新のJavaScriptの構文やAPIが利用できることが挙げられます。

Chromeは標準仕様への対応が早く、const宣言やアロー関数およびasync/await関数などの最新の構文を使用できます。ブラウザ操作を自動化する際には、ブラウザ上で実行するJavaScriptを記述することがあります。そのような場合に、最新の構文やAPIを使用することで、コードを安全かつ簡潔に記述できるようになります。

ヘッド「フル」モード

開発する際に見過ごせない機能が、ヘッド「フル」モード、すなわち通常のChromeとしてGUIを立ち上げることができるという点です。

ヘッドレスモードでしか実行できないPhantomJSでは、デバッグのためにログをたくさん仕込んだり、スクリーンショットを何度も撮影する場面がよく見られます。一方Chromeでは、ヘッドレスモードを解除することで、いつもと同じように画面上で何が起きているかを把握できるので、デバッグに役立てることができます。

基本機能の違い

そのほかにも、いくつか仕様面で大きな違いは見られます。

たとえば、PhantomJSではタブの概念がないため常に「1プロセス=1タブ」で動作しますが、ヘッドレスChromeでは複数のタブを同時に開くことができます。

また、PhantomJSには実装されている、別ページに遷移させないためのナビゲーションロック機能が、ヘッドレスChromeではサポートされていない(執筆時2019年1月現在)などの違いがあります。

しかし、このようなPuppeteerの機能不足は、今後少しずつ解消されていくと考えられます。

Puppeteerの魅力

ヘッドレスChromeを操る方法は大きく分けて2つあります図3⁠。一つはSeleniumのようにWebDriverを通じて操作する方法、もう一つはPuppeteerのように直接操作する方法です。

図3 ヘッドレスChromeを操る方法
図3

WebDriverを間に挟むことで、同じインタフェースに対応しているさまざまなブラウザを、同一のスクリプトで同じように操作できます。一方、ブラウザごとに異なるAPIを使って直接操作すると、ほかのブラウザとの互換性が失われる代わりに、そのブラウザ固有の機能を利用できます。

Seleniumは幅広いブラウザをサポートするために、多くの機能を犠牲にしています。一方、PuppeteerはヘッドレスChromeに特化することで、Seleniumにはないさまざまな機能をサポートしています。実際のところ、どちらか一方が常に優れているということはなく、目的に応じた使い分けが必要です。

しかし、PuppeteerにはSeleniumにはない魅力が数多く存在します。そこで、それらの魅力を一つずつ紹介していきます。

大きなコミュニティ

Puppeteerは、Chromeデベロッパーツールの開発チームによってメンテナンスされています。開発チームのGoogleブランドによる安心感もたしかに無視できませんが、それ以上に大きなオープンソースコミュニティによって支えられています。

GitHub上でも、Seleniumの3倍以上となる4万件を超えるStarが付けられており(執筆時⁠⁠、今最も勢いのあるオープンソースプロジェクトの一つです。

簡単なセットアップ

Seleniumを使ってヘッドレスChromeを扱うためには、Seleniumだけでなく、WebDriverをインストールし、対応するChromeのバージョンをそろえるなどの準備が必要です。それに対してPuppeteerは、Puppeteerさえインストールすれば、最新のChromium(Chromeを構成するオープンソースソフトウェア)が一緒にダウンロードされます。バージョン間の差異に悩まされないことも、大きな魅力の一つです。

ChromeおよびChromiumは、いずれもヘッドレスモードで起動できます。しかし、最新のバージョンを使用でき、さらにブラウザ自動化に必要のない機能が削ぎ落とされたChromiumのほうが、より多くの場合で最適な選択になるでしょう。

なお、どちらを使っても解説内容は大きく変わらないため、本特集ではChromiumも含めてChromeと表記しています。

充実したデバッグ

ヘッドレスChromeの機能としてヘッド「フル」モードでの起動を挙げましたが、実際にはこのモードで起動するだけでは、すべてのイベントが一瞬で通り過ぎてしまうため、何が起きたかを把握することは容易ではありません。

しかし、スローモーションで再生することや、デバッガを仕込んで開発者ツールを開くことが可能になっており、Puppeteerにはデバッグのための機能が充実しています。なお、デバッグの具体的な手順については、第2章で取り上げます。

豊富な機能

Puppeteerには実用的な機能が豊富に取りそろえられています。これらの機能の中には、リクエストやレスポンスを書き換えたり遮断できるものがあり、外部サービスのモックを用意するなどに役立ちます。

また、パフォーマンスやカバレッジの計測といった、利用者にとって便利な機能が常に追加され続けています。これらのユニークな機能については、第3章以降で取り上げていきます。

信頼性の高いコード

Puppeteerを使用することで、SPAに対しても信頼性の高いコードを書くことができます。

Seleniumを含むほとんどのライブラリでは、アンカーリンクのクリックやHistory APIの呼び出しは、画面遷移として扱われません。そのため、画面遷移が確認できるまでsetTimeout()を実行するといった、冗長なコードを挿入しなければならない場面がよく見られます。このようなコードは、可読性が低いだけでなく、コードの実行結果も不安定になってしまいがちです。

しかし、PuppeteerはURLが切り替わるすべてのイベントを画面遷移と定義しているため、SPA専用の特別なコードを必要とせず、シームレスに動作させることができます。

まとめ

第1章では、ヘッドレスブラウザが求められる背景から、ヘッドレスChromeの特徴とPuppeteerが持つ魅力を順に紹介しました。本章を通じて、コーディングに向けての期待も高まったのではないかと思います。そこで次章からは実際の利用方法を解説していきます。

おすすめ記事

記事・ニュース一覧