Puppeteerは、Headless Chromeを操作できるNode.jsのライブラリです。Chrome DevToolsチームがメンテナンスを行なっており、スクレイピングやフロントテストに活用できます。ここでは、Puppeteerの基本的な使い方を確認します。
前知識
- ヘッドレスブラウザとは?
- GUI(画面)を持たないブラウザです。
- Chromiumとは?
- Google Chrome の元となっているオープンソースのブラウザです。
- Chromium – Wikipedia
Puppeteerをインストール
Chromiumを含める
nodeのversionは以下の通りです。
$ node -v
v8.10.0
Puppeteerをインストールします。
$ yarn add puppeteer
$ npm install --save puppeteer
Puppeteerをインストールすると Chromium
も同時にダウンロードされます。
macでインストールした場合、以下フォルダに Chromium
が存在します。
$ ls -l node_modules/puppeteer/.local-chromium/mac-624492/chrome-mac/Chromium.app/Contents/MacOS
total 368
-rwxr-xr-x 1 xxx xxx 186028 Feb 22 12:02 Chromium
Chromiumを含めない
Chromium
は容量が大きいので注意が必要です。例えば AWS Lambda
で利用する場合、Chromium
を含めてしまうとデプロイパッケージの容量制限に引っかかります。
そこで、 Chromium
を含めないでPuppeteerをインストールする方法があります。
方法1
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD
という環境変数を設定した状態でインストールすると Chromium
が含まれません。
$ PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 yarn add puppeteer
方法2
puppeteer-core
という Chromium
を含まないパッケージがあります。
$ yarn add puppeteer-core
動作例
sample1.js
というファイルで、puppeteerを利用してみます。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false, // 動作確認するためheadlessモードにしない
slowMo: 500 // 動作確認しやすいようにpuppeteerの操作を遅延させる
})
const page = await browser.newPage()
await page.goto('https://www.google.com/')
await page.type('input[name=q]', 'スカイツリー', { delay: 100 })
await page.click('input[type="submit"]')
await page.waitForSelector('h3 a')
await page.screenshot({ path: 'screenshot/sample1.png' })
await browser.close()
})()
以下の処理を行っています。
https://www.google.com/
にアクセスして、スカイツリー
の検索結果のスクリーンショットをとり、screenshotディレクトリ
に保存
実行します。
$ mkdir screenshot
$
$ node sample1.js
$
$ ls screenshot
sample1.png
headless: false
としたので、画面上で動作を確認することができます。
要素取得
PageクラスとElementHandleクラス
以下のメソッドを利用して、要素を取得できます。
メソッド | Class: Page | Class: ElementHandle | 概要 |
---|---|---|---|
evaluate | ◯ | × | JavaScriptで処理できる。 |
$ | ◯ | ◯ | 要素( ElementHandle )を取得。内部で querySelector が利用されいている。 |
$$ | ◯ | ◯ | 複数の要素( Array<ElementHandle> )を取得。内部で querySelectorAll が利用されいている。 |
$eval | ◯ | ◯ | JavaScriptで処理できる。Elementプロパティ を利用できる。 |
$$eval | ◯ | ◯ | JavaScriptで処理できる。Elementプロパティ を利用できる。 |
- Pageクラス
- ElementHandleクラス
- Element
利用例
要素取得の実装例を示します。
<div id="result">
<div class="item">
<a href="http://xxx" data-test="aaaa"></a>
<p>xxxxxxxx</p>
</div>
<div class="item">
<a href="http://yyy" data-test="bbbb"></a>
<p>yyyyyyyy</p>
</div>
<div class="item">
<a href="http://zzz" data-test="cccc"></a>
<p>zzzzzzzz</p>
</div>
</div>
上記HTMLから要素を取得する処理を実装します。
const puppeteer = require('puppeteer')
const path = require('path')
const testItem = async page => {
console.log('############ 要素を1つ取得 ############')
// ElementHandleクラスのインスタンスを取得
const item = await page.$('#result div.item')
console.log(await (await item.getProperty('innerHTML')).jsonValue())
console.log('--------------------------------------')
console.log(await (await item.getProperty('textContent')).jsonValue())
}
const testItems = async page => {
console.log('############ 要素を1つ取得 ############')
// Array<ElementHandle> を取得
const items = await page.$$('#result div.item')
console.log(items.length)
for (const item of items) {
// data属性取得
console.log('--------------------------------------')
console.log(await item.$eval('a', element => element.getAttribute('data-test')))
// href取得
const aTag = await item.$('a')
const href = await aTag.getProperty('href')
const url = await href.jsonValue()
console.log(url)
}
}
(async () => {
const browser = await puppeteer.launch()
// Pageクラスのインスタンスを取得
const page = await browser.newPage()
await page.goto(`file:${path.join(__dirname, '/test_html/sample.html')}`)
await testItem(page)
await testItems(page)
await browser.close()
})()
実行結果は以下の通りです。
$ node sample2.js
############ 要素を1つ取得 ############
<a href="http://xxx" data-test="aaaa"></a>
<p>xxxxxxxx</p>
--------------------------------------
xxxxxxxx
############ 要素を1つ取得 ############
3
--------------------------------------
aaaa
http://xxx/
--------------------------------------
bbbb
http://yyy/
--------------------------------------
cccc
http://zzz/
サーバーレスで実行する方法
Cloud Functions
サポートされているようです。
AWS Lambda
Chromium
が必要とする共有ライブラリがLambda上に存在しないので対策が必要です。
serverless-chrome というのを利用すると、Lambda上でも実行できます。
serverless-chrome
を利用して、Lambda上で動かすための下準備をしてくださったリポジトリがあったので、こちらを活用してみると良いと思います。
https://github.com/sambaiz/puppeteer-lambda-starter-kit
参考・補足
- https://pptr.dev/
- https://github.com/GoogleChrome/puppeteer
- API Documentation
- 利用例
- ブラウザでの操作を記録してコードを生成してくれるChrome拡張拡機能もあります。