🙄
動的サイトをスクレイピングしたい
やりたいこと
- 動的サイトをスクレイピングしたい
- Lambda上で動かせるようにしたい
調べたこと
- chatGPTにライブラリを聞く
- Puppeteerというものがあるっぽい
- 良さげな記事を発見したのでこれを元に作成する
実際に触る
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);
};
- ライブラリが読み込めないとエラーが出たので、原因調査
- https://qiita.com/hirorin/items/dba5ef8fcf570ac3a9e4
- nodejsという名前に展開後ならないと行けないとのこと。
- 再度レイヤーを作成する
- versionアップという形を取る
- タイムアウトエラー
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以上にする必要がある
- 次にこのリポジトリを見る
- https://github.com/shelfio/chrome-aws-lambda-layer
- 公開されているLambdaレイヤーがあるため、ありがたくそれを使ってみる。
- サンプルコードを動かす
- ちゃんとここのREADMEを見る
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