🌐

PuppeteerとCloud Functons for Firebaseでスクレイピング

2024/05/28に公開

Node.js v18、Puppeteer v22とCloud Functons for Firebase Gen2 環境でスクレイピングする最小構成を作ってみます。

https://github.com/takamoso/puppeteer-firebase-cloud-functions

GitHub にプロジェクト全体のコードが置いてあります。

インストール

Firebase プロジェクトの作成と firebase init までの作業は省略します。

npm i puppeteer puppeteer-chromium-resolver

スクレイピングするための puppeteer とローカルに Chromium をダウンロードしてインストール先のパスを動的に取得できるようになる puppeteer-chromium-resolver をインストールします。

Puppeteer は v19 から node_modules 内に Chromium をダウンロードしないように破壊的変更されたため、Cloud Functons for Firebase 上で実行時に Chromium が見つからないエラーが発生します。それを解決してくれるのが puppeteer-chromium-resolver です。

/firebase.json
{
  "functions": [
    {
      "source": "./",
      "codebase": "default",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log"
      ]
    }
  ]
}

今回は、プロジェクトのディレクトリ直下に index.js を作成するので、source./ を指定しておきます。

スクレイピングの処理

プロジェクトのディレクトリ直下に index.js を作成します。

/index.js
import {onRequest} from 'firebase-functions/v2/https'
import puppeteer from 'puppeteer-chromium-resolver'

export const scrape = onRequest({
  // メモリは最低でも1GB推奨だがギリギリだと処理が中断することがあるため余裕を持たせておく
  memory: '2GiB',
}, async (req, res) => {
  // ローカルにインストールされたChromiumを解決する
  const stats = await puppeteer()
  // ブラウザを起動する
  const browser = await stats.puppeteer.launch({
    // Chromiumの実行パス
    executablePath: stats.executablePath,
    args: [
      '--disable-gpu',
      '--disable-dev-shm-usage',
      '--disable-setuid-sandbox',
      '--no-first-run',
      '--no-sandbox',
      '--no-zygote',
      '--single-process',
    ],
    // 古いヘッドレスモード(パフォーマンスがいい)
    headless: 'shell',
  })
  // 新規ページを開く
  const page = await browser.newPage()

  // ページにアクセスする
  await page.goto('https://zenn.dev/takamoso', {waitUntil: 'domcontentloaded'})

  // ページタイトルを出力
  res.send(await page.title())

  // ブラウザを閉じる
  await browser.close()

  // レスポンスを返す
  res.end()
})

Zenn の個人ページにアクセスして、ページのタイトルを出力する関数です。onRequest() 関数を使うときは必ず res.send('')res.end() でレスポンスを返すようにします。また、Puppeteer の処理が終わったら、await browser.close() でブラウザを閉じる処理を書きます。これを忘れるとページがメモリ上に確保されたまま蓄積して行くので、メモリエラーになります。

npm run shell

> shell
> firebase functions:shell

✔  functions: Using node@18 from host.
Serving at port 8238

i  functions: Loaded functions: scrape
⚠  functions: The following emulators are not running, calls to these services will affect production: firestore, database, pubsub, storage, eventarc
firebase > scrape()

npm run shell コマンドでエミュレーターが起動するので、scrape() と入力して関数を実行します。

RESPONSE RECEIVED FROM FUNCTION: 200, たかもそさんの記事一覧 | Zenn

こんな感じでレスポンスが返ってこれば成功です。

デプロイ

npm run deploy コマンドでデプロイします。

npm run deploy

本番環境でも実行されるかどうかを確認していきます。

Google Cloud Platformにアクセスして、左上のメニューから Cloud Functions をクリックします。

関数名の scrape をクリックします。

URL をクリックすると新規タブで関数が実行されます。

少し時間はかかりますが、ページタイトルが表示されれば成功です。

Discussion