🙄

動的サイトをスクレイピングしたい

2023/04/18に公開

やりたいこと

  • 動的サイトをスクレイピングしたい
  • Lambda上で動かせるようにしたい

調べたこと

実際に触る

npm install

npm install chrome-aws-lambda --save-prod
npm i puppeteer-core --save
  • トップディレクトリで、zipファイルを作って、Lambdaにアップロードする
    • 容量が多い場合はS3にアップロードして参照しないとダメなので、S3にアップロード後レイヤーを作成する
    • 成功したので実際にコードを書いてみる(一旦サンプルコードと同じ内容を書く)
const chromium = require('chrome-aws-lambda');
exports.handler = async (event, context, callback) => {
const url = 'https://example.com';
    const browser = await chromium.puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    await page.goto(url, { waitUntil: 'networkidle0' });
    const pdfConfig = {
        path: 'example.pdf', // Saves pdf to disk.
        format: 'A4',
        printBackground: true,
    };
    await page.emulateMediaType('screen');
    const pdf = await page.pdf(pdfConfig);
    await browser.close();
return callback(null, pdf);
};
  • ライブラリが読み込めないとエラーが出たので、原因調査
  • タイムアウトエラー
Response
{
  "errorMessage": "2023-04-17T23:47:56.277Z xxx Task timed out after 3.07 seconds"
}
  • もう少しかんたんなサンプルを見つける
    • chatGPTに聞く
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://www.google.com');
  const title = await page.title();
  console.log('Page title:', title);
  await browser.close();
})();
  • これを試す
    • これでもエラーが出るので他の原因っぽいので、chatGPTに聞く
lambdaでPuppeteerを使うと下記のエラーが出ます。

Response
{
  "errorMessage": "2023-04-17T23:52:12.216Z 8199ee30-062b-429a-98bf-f32e135b82d3 Task timed out after 3.06 seconds"
}

コードの修正点を教えて下さい。

const chromium = require('chrome-aws-lambda');
exports.handler = async (event, context, callback) => {
const url = 'https://example.com';
    const browser = await chromium.puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    await page.goto(url);
    const title = await page.title();
    await browser.close();
    
    console.log(title);
return title;
};

---

Lambda関数がタイムアウトしてしまっているようですね。Lambda関数のデフォルトのタイムアウト時間は3秒ですので、それを超えた場合にはタイムアウトエラーが発生します。

Puppeteerは起動に時間がかかるため、Lambda関数のタイムアウト時間を延長する必要があります。以下のように、Lambda関数の設定でタイムアウト時間を延長してください。

AWSコンソールにログインし、Lambda関数のページを開きます。
「構成」タブを選択します。
「一般設定」をクリックします。
「タイムアウト」の設定を変更します。
「変更を保存」をクリックします。
タイムアウト時間を変更した後、再度Lambda関数を実行してみてください。それでも問題が解決しない場合は、Puppeteerの起動に時間がかかるため、Lambda関数の起動時にブラウザを事前に起動しておく方法があります。詳細については、PuppeteerとAWS Lambdaを使用するための公式のチュートリアルを参照してください。
  • Lambda側のタイムアウト時間の設定をいじればいい。(ど忘れしてた)

    • 1分にしてみて、再度実行する
    • エラー起きる
  • https://github.com/Sparticuz/chrome-aws-lambda

    • ちゃんとここのREADMEを見る
      • You should allocate at least 512 MB of RAM to your Lambda, however 1600 MB (or more) is recommended.
      • Lambdaの容量を512MB以上にする必要がある
    • 次にこのリポジトリを見る
const chromium = require('@sparticuz/chrome-aws-lambda');

exports.handler = async (event, context, callback) => {
  let result = null;
  let browser = null;

  try {
    browser = await chromium.puppeteer.launch({
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath,
      headless: chromium.headless,
      ignoreHTTPSErrors: true,
    });

    let page = await browser.newPage();

    await page.goto(event.url || 'https://example.com');

    result = await page.title();
  } catch (error) {
    return callback(error);
  } finally {
    if (browser !== null) {
      await browser.close();
    }
  }

  return callback(null, result);
};

デプロイしてテストを動かす

Response
"Example Domain"

来た!

まとめ

  • これで、Lambda上で動的サイトをクローリングできるようになった。
    • 容量だったりタイムアウト値を変更していたりするので、料金には要注意かも

参照リンク

Discussion