はじめに
Headless ChromeをNode.js上で操作するライブラリにPuppeteerがあります(しかもGoogle製!)。
これがスクレイピングツールとしても使えそうなので、試しにWikipediaからタイトル、本文、画像を取ってみました。
インストール手順
OS : Windows10
Puppeteerのバージョン : 1.5.0
インストールは以下のコマンドを打つだけです。
$ npm install puppeteer
※事前にNode.jsとnpmをインストールする必要があります。
ソースコード(全体)
const puppeteer = require('puppeteer');
(async () => {
// const browser = await puppeteer.launch();
const browser = await puppeteer.launch({ headless: false }); // ヘッドレスにしない
const page = await browser.newPage();
const url = 'https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%87%E3%83%AA%E3%83%BC%E3%83%9A%E3%83%B3%E3%82%AE%E3%83%B3';
await page.goto(url, { waitUntil: 'domcontentloaded' }); // domを読み込むまで待機
// タブを移動
const pages = await browser.pages();
const detailPage = pages[1];
await detailPage.bringToFront();
// タイトル取得
let selector = '#firstHeading';
const titleDom = await detailPage.$(selector);
if (titleDom) {
title = await detailPage.$eval(selector, e => e.textContent);
console.log(title);
}
// 本文取得
selector = '#mw-content-text > div > p';
const contentDom = await detailPage.$$(selector);
if (contentDom.length > 0) {
content = await detailPage.$$eval(selector, elements => {
let datas = [];
for (e of elements) {
datas.push(e.textContent);
}
return datas;
});
for (c of content) {
console.log(content);
}
}
// 画像取得
selector = '#mw-content-text > div > table.infobox.bordered > tbody > tr:nth-child(2) > td > a > img';
const dom = await page.$(selector);
if (dom) {
await dom.screenshot({ path: 'penguin.png' });
}
await browser.close();
})();
ソースコード(詳細)
覚えておいたほうがよさそうなところだけメモ。
読み込むまで待機
page.goto()でページ遷移する際に、waitUntilでページのロードを待機します。
-
domcontentloaded
にすれば、htmlを読み込むまで待機 -
load
にすれば、その後画像を読み込むまで待機 -
networkidle0
にすれば、500ミリ秒コネクションがなくなるまで待機
JavaScriptを読み込むまで待とうと思ったら、networkidle0
がよさげ?
networkidle2
ってのもある模様。
await page.goto(url, { waitUntil: 'load' });
タブの移動
リンクをクリックすると、新規タブに移動される場合があります。
その場合はそのタブに移動する必要があります(今回のソースではいらないけど・・・)。
const pages = await browser.pages();
const detailPage = pages[1];
await detailPage.bringToFront();
スクレイピング
テキストや属性を取得するときは、基本この書き方だと思います。
この例だとtextContentを指定しているので要素内のテキストを取得します。
title = await detailPage.$eval(selector, e => e.textContent);
画像の取得
DOMに対してもスクリーンショットが取れます。もちろんページにも取れます。
await dom.screenshot({ path: 'penguin.png' });
実行
$ node crawler.js
なんか、画像がうまく取れませんでした・・・。
こういう時は、画像のURLにアクセスしてからpage.screenshot()をするとよさそうです。
おわりに
割と簡単で便利でした。
PuppeteerはAPIの種類がかなり多いので、色々試してみるのも面白そうですが、公式のAPIドキュメントを呼んでもよく分からないところもまだありますね・・・。