GMOアドマーケティングのy.a.です。
PuppeteerはHeadless Chrome使うのを便利にするNode ライブラリです。
https://developers.google.com/web/tools/puppeteer/
(引用元:Puppeteer | Tools for Web Developer https://developers.google.com/web/tools/puppeteer/)
ですのでスクレイピングなり、なにかWebページのテストを自動化したい場合に、比較的手軽に色々できます。
そこでPuppeteerに興味を持った方へ、なるべく簡潔にPuppeteerで出来ることをいくつか紹介したいと思います。
※なお実行環境にNode.jsがインストールされていることが前提です。
インストール
1 |
npm install --save puppeteer |
基本的な定義
main.js
1 2 3 4 5 6 7 8 9 10 11 12 |
const puppeteer = require('puppeteer'); const url = '' // 任意のURL; (async function(){ const browser = await puppeteer.launch(); const page = await browser.newPage(); const response = await page.goto(url); // ここに以下紹介するものを入れるイメージです。 await browser.close(); })(); |
実行するには下記コマンド
1 |
node main.js |
スクリーンショットはできる?
できます。page.screenshotを使用してできます。とても簡単です。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagescreenshotoptions
1 |
await page.screenshot({path: 'screenshot.png'}); |
上記はページ全体ですが、特定の要素だけ取るということも可能です。elementHandle.screenshotです。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandlescreenshotoptions
例えばh1要素だけのスクリーンショットを撮りたい場合はこうです。
1 2 |
const element = await page.$('h1'); await element.screenshot({path: 'screenshot_h1.png'}); |
JS実行できる?
できます。page.evaluateを使用します。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageevaluatepagefunction-args
こういう使い方をすることはないと思いますが、こんな感じでJS書けます。
1 2 3 4 |
await page.evaluate(() => { const h1 = document.querySelector('h1'); h1.textContent = 'hoge'; }); |
クリックして遷移は?
できます。page.clickです。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageclickselector-options
1 |
await page.click('.button_element'); |
あるいはelementHandle.clickもありますので、それに倣うならこうやってもいけますね。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandleclickoptions
1 2 |
let button_element = await page.$('.button_element'); await button_element.click(); |
待つことは?
できます。page.waitForNavigationです。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions
page.goto()のデフォルトはloadなので、使う場面としてはclickしてページ遷移した後だと思います。
下記はDOMContentLoadedまで、次の処理を行わないというものです。
1 |
await page.waitForNavigation({waitUntil: 'domcontentloaded'}); |
他にも、特定の要素を待つことなどもできます。waitForSelectorです。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitforselectorselector-options
下記はlazyクラスが付与された要素が出るまで待つ場合。
1 |
await page.waitForSelector('.lazy'); |
ただやろうと思えば、page.waitForでほぼ全てこちらでまとめられそうです。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitforselectororfunctionortimeout-options-args
1 |
await page.waitFor('.lazy'); |
他にもtimeoutなど。下記は5000ms待つ場合です。
1 |
await page.waitFor(5000); |
DOMレンダーツリーを取得
GoogleChromeのDevToolで言うところの「Elements」パネルで表示されるものも、取得できます。
先に紹介したpage.evaluateで、JS実行できることがわかっているので、あとはそれを応用してみましょう。
下記はloadイベント後のDOMレンダーツリーです。
1 2 3 |
const bodyHandle = await page.$('body'); const html = await page.evaluate(body => body.innerHTML, bodyHandle); console.log(html); |
リダイレクト検知
できます。request.redirectChainを使用します。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#requestredirectchain
page.gotoがResponseクラスというPromiseオブジェクトを返しますので、それを利用します。
1 2 3 4 5 6 7 |
const response = await page.goto(url); // ←本稿冒頭で紹介した定義 const chain = response.request().redirectChain(); console.log(chain.length) // リダイレクト回数 chain.forEach((chain) => { console.log(chain.url()); // リダイレクトのあったURL }); |
fileプロトコルも取得できる?
できます。page.gotoの引数に fileプロトコルでパスを指定すれば良いだけです。 file://path/to/html
ちなみにHTMLのstringだけ渡してもいけます。これはsetContentを使用します。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagesetcontenthtml-options
1 2 |
const html = '<div style="width: 100px;height: 100px;background: #000;color: #fff">hoge</div>'; await page.setContent(html); |
おまけ
async/await
紹介したサンプルコードを見てもわかると思うのですが、async/await(ES2017)がとてもよく出てきます。これは非同期処理のために必要なものです。
使い方についてほんとにざっくり説明すると、
functionの前にasyncを定義することで非同期関数になりまして、その中で呼び出す別の非同期関数の前にawaitをつければ、その処理が返るまで待ちます。
再掲しますが、最初にサンプルとして出したものも、そういった形で作られていることが分かると思います。
1 2 3 4 5 6 7 8 9 |
(async function(){ const browser = await puppeteer.launch(); const page = await browser.newPage(); const response = await page.goto(url); // ここに以下紹介するものを入れるイメージです。 await browser.close(); })(); |
ただこの辺はJavaScriptのPromise(ES2015)の理解が前提ですので、
PuppeteerやNode.js使う際は、ES2015以降の新シンタックスなどは押さえておくとよいと思います。
独特に感じるAPI
APIドキュメントを見ていると
「page.$()」や「page.$$()」など出てきます。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageselector
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageselector-1
それぞれdocument.querySelector、document.querySelectorAllと同等のものなんですが、ちょっと見ない感じだな、と思っていたらどうやらCommand Line APIのようでした。
https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference?hl=ja
つまりブラウザのコンソールでも使えるんですね。
jQueryを読み込んでいる場合、$はjQueryオブジェクトになるわけですが、そうでない場合はこのCommand Line APIとして使えるわけです。
その他PuppeteerではcreateCDPSessionといった Chrome Devtools Protocolを使えるAPIも用意されています。
つまり普段開発でChromeを使用している人にとってもChromeの機能をほぼ不足なく使えそうです。
https://chromedevtools.github.io/devtools-protocol/
ということで私もまだまだ使い倒したいと思います。
それでは。