カテゴリー別アーカイブ: 佐藤寛之

PuppeteerによるWebクローリング 基礎編

あけましておめでとうございます。
今日で入社から1年になりましたシニアエンジニアの佐藤寛之です。

最近公私ともにWebクローリングを扱う機会が増えてきました。世の中のWebシステムには人間が使うことは想定していても、プログラムによる処理を想定せずAPIはおろかデータエクスポート機能すらないものも珍しくありません。そんなシステムからデータを収集したり他システムと連携させたりするのに、ブラウザの自動操作によるクローリングは非常に便利です。

クローリングといえば以前はPhantomJSが主流でしたが、PhantomJSはすでにメンテナンスが終了しており、現在はPuppeteerが広く使われています。PuppeteerはGoogle Chromeのヘッドレスモードを利用するNodeのライブラリで、Googleがメンテナであるため最新のChromeの機能に追随しているのが大きなアドバンテージと言えます。

ところで私は趣味で吹奏楽をやっているのですが、リハーサル会場を確保するのはなかなか大変な作業です。空いている施設を探し回る不毛な作業をどうにかするため、Webクローリングで施設の空き状況を収集するシステムを作って運用しています。よくお世話になっている静岡県沼津市の沼津市民文化センターと千本プラザの空き状況を調べる部分のコードを例として、Puppeteerの基本的な使い方をご紹介します。

Node9.5とPuppeteer1.11.0の環境で運用しています。

インストール

$ yarn add puppeteer

以上です。

基本形

ここではPuppeteerの Browser オブジェクトと Page オブジェクトを使っています。 Browser はChromeブラウザのプロセスそのもの、 Page はブラウザで開いたWebページをそれぞれ制御するためのオブジェクトです。

ページ遷移


対象がSPAでなければ、この書き方をすると指定したURLに遷移してページの表示完了まで待機できます。ページの読み込みが不完全な状態で次の処理を行うと、処理対象のHTML要素にアクセスできず例外が発生するので、適切に「待つ」というのがPuppeteerを使う上での肝になります。

上記では待機条件として domcontentloaded を指定して、ページ内のDOMツリーが完全に構築されるまで待機しています。この場合、待機後はすべてのHTML要素にアクセスできる状態になりますが、外部リソース(画像、JavaScript、CSSなど)の読み込みは待ちません。スクリーンショットの取得やページ内のJavaScriptの実行などが必要な場合は networkidle0 を指定するとネットワーク通信が完全になくなるまで待機することができ、これにより全てのリソースの読み込みが完了してから先に進むことが可能になります。

要素の取得


ページ内のHTML要素を取得する例です。沼津市で導入している施設予約システムはidもclassもまともに振られていないのでなかなか険しい感じになっています(Chromeの開発者ツールでコピペしました)。
$$$メソッドでCSSセレクタを指定すると、それにマッチするHTML要素を表す ElementHandle オブジェクトを取得できます。 $ はセレクタにマッチする最初の要素を、 $$ はセレクタにマッチする全ての要素の配列を返します。 Page オブジェクトの $$$はページ内の全ての要素が、 ElementHandle オブジェクトの $$$ はそのHTML要素の子要素が取得対象になります。
上記例はページ内に1つだけであろうカレンダー部分の tbody を取得してから、その中にある全てのリンクを取得しています。

属性値の取得


ElementHandle オブジェクトから属性値を取得する例です。 a 要素の href 属性を取得しています。よくある操作のはずですがなかなかトリッキーな書き方になります。

クリック


リンクやボタンなどをクリックする例です。対象はCSSセレクタで指定します。リンクのクリック後のページ遷移の待ち方は、URLを指定したページ遷移と同じです。

ブラウザ上でのJavaScriptの実行


Page オブジェクトの evaluate メソッドを使うと、Nodeではなくブラウザ上で任意のスクリプトを実行し、その結果を取得することができます。
上記例はページ内の3つのセレクトボックスから値を取得して返しています。この程度であればPuppeteerのAPIを通しても同じことはできますが、この方法を使うと事実上何でもできるので、Puppeteerが提供していない高度な処理が必要な場合や、Webページに埋め込まれたJavaScriptを実行する必要がある場合、APIドキュメントを掘り返すのがめんどくさいときなどに使います。

だいたいここまでで説明したような機能を使って実装した沼津市施設予約システムのクローラーはこんな感じになります。実際のシステムにはcronでこれを叩くバッチと結果を表示する簡単なフロントエンドが付いています。

シンプルなクローラーはこのくらいの知識で十分ですが、サイトの構造や実行環境によってはより高度なテクニックを使う必要が出てきます。そういった応用編も後日公開しようと思います。