Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Puppeteer + GCP Functionsでサーバレスなスクレイピング

More than 1 year has passed since last update.

サーバレスな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はこんな感じ。

index.js
const scraper = require('./functions/scraper');
exports.scraper = scraper;

あとはfunctionsディレクトリの下にscraper.jsを作り、そこにコードを作成していきます。
今回はasyncを使いたいのでnode8系で書きます。

scraper.js
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に記述しておけば、デプロイ時に環境変数に設定できます。

.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などにも入れられるので、大規模な運用もできるかもしれません。

参考

awa2
アンテナを世界中に立てたいマン
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away