サーバレスなScraperを運用したい人向け。
GCP FunctionsでPuppeteerが動くぞ!
2018年の8月から、GCPのGoogle App EngineとGoogle Functionsで Puppeteer がサポートされました。
Puppeteerは、nodejsで動くHeadless Chrome(Chromium) Driverです。
準備
インストール
$ npm init
$ npm install -S puppeteer
コード
まず、エントリーポイントになるindex.js
はこんな感じ。
const scraper = require('./functions/scraper');
exports.scraper = scraper;
あとはfunctionsディレクトリの下にscraper.js
を作り、そこにコードを作成していきます。
今回はasyncを使いたいのでnode8系で書きます。
module.exports = async (req, res) => {
const USERNAME = process.env['USERNAME'];
const PASSWORD = process.env['PASSWORD'];
const LOGIN_URL = 'https://example.com';
// Chromiumを起動し、ページをつくる
const browser = await puppeteer.launch({ args: ['--no-sandbox'] });
const page = await browser.newPage();
page.setViewport({ width: 1280, height: 1024 });
// ログインページに移動し、ログインする
await page.goto(`${LOGIN_URL}/login`, { waitUntil: 'domcontentloaded', timeout: 20000 });
await page.type('input#email', USERNAME);
await page.type('input#password', PASSWORD);
await page.click('#login-btn-sumit');
await page.waitForNavigation();
await page.goto('`${LOGIN_URL}/users`', { waitUntil: 'domcontentloaded', timeout: 20000 });
// スクレイピングの一例
// user_info_fieldsクラスのtrタグをパースします
const elements = await page.$$('tr.user_info_fields');
const users = [];
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const userid= await element.$eval('th', e => e.innerText);
const username = await element.$eval('td.username span', e => e.innerText);
const email = await element.$eval('td.email', e => e.innerText);
users.push({
userid: userid,
username: username,
email: email
});
}
res.send(users);
}
環境変数
Functionsでは.env.yaml
に記述しておけば、デプロイ時に環境変数に設定できます。
USERNAME : 'me@example.com'
PASSWORD : 'passwd1234!'
デプロイ
GCP SDKとFunctionsエミュレータをインストールしておいてください。
Functionsエミュレータは次の通り。
$ npm install -g @google-cloud/functions-emulator
$ functions start
ローカルでの実行とデプロイは次の通り。
// ローカルデプロイ
$ functions deploy scraper --trigger-http"
// ローカル実行
$ functions call scraper
// 本番デプロイ
$ gcloud beta functions deploy scraper --trigger-http
Functions側をnode v8に設定しないとデプロイがコケます。
詳しいオプションは、こちらとかを見てください。
まとめ
お疲れ様でした。
Functions特有の癖もありますが、サーバレスでHeadless Chromiumを動かせるのはいろいろ応用が広がりそうです。FunctionsからGCSやCloud Store, BigQueryなどにも入れられるので、大規模な運用もできるかもしれません。