はじめに
AWSの Lambda
と EventBridge
を使って、FrontierのBTOセールページのキャプチャを定期的にDiscordに送信する仕組みを作成する。
Python3.12
環境で試みたがかなり面倒そうなので、puppeteer-core
にしてみた。
それでも、都合の悪い部分が出たので playwright-core
を使うことにした記事です。
環境
AWS Lambda
Node.js 20.x
playwright-core
準備
- AWSアカウント
- Discordサーバのチャンネル
DiscordサーバのWebHookURLを取得する
作成済みのチャンネルにて、設定画面を開く。
作成したウェブフックを選択し、「ウェブフックURLをコピー」でURLをコピーして控えておく。
上記で、ウェブフックURLを取得できる。
WebHook URLの動作確認
curl
での動作確認を行う。
curl -H "Content-Type: application/json" \
-X POST \
-d '{"content": "Test Message!"}' \
[取得したWebHook URL]
Lambdaレイヤー用のS3バケットの作成
- AWSコンソールから「S3」サービスを選択する。
- 適当な名前をつけてバケットを作成する。
今回は、lambda-layer-for-nodejs
という名前をつけた。
全体の流れ
AWS LambdaにNode.jsのコードをデプロイ
EventBridgeにて定期的にLambda関数を実行できるように設定
動作確認
Lambda構築
Lambdaにコードを作成する
AWSコンソールにて、「Lambda」サービスを開く
「関数の作成」を選択し、作成画面を開き、下記のように作成する。
3. 作成後、index.js
に下記のコードを貼り付ける。
const chromium = require('@sparticuz/chromium');
const { chromium: playwright } = require("playwright-core");
const axios = require('axios');
const FormData = require('form-data');
exports.handler = async (event, context, callback) => {
const targetUrl = process.env.TARGET_URL;
const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
let result = null;
let browser = null;
try {
browser = await playwright.launch({
args: chromium.args,
defaultViewport: null,
executablePath: await chromium.executablePath(),
headless: chromium.headless === 'true',
});
const page = await browser.newPage({
ignoreHTTPSErrors: true,
});
await page.goto(event.url || targetUrl, {waitUntil: 'domcontentloaded'});
await page.mouse.move(0, 0)
await autoScroll(page);
const screenshotBuffer = await page.locator('div.block-event-page--accessory').screenshot({
fullPage: true,
});
// Discordにスクリーンショットを送信
const formData = new FormData();
formData.append('file', screenshotBuffer, {
filename: 'screenshot.png',
contentType: 'image/png'
});
await axios.post(webhookUrl, formData, {
headers: {
...formData.getHeaders(),
},
});
await browser.close();
} catch (error) {
if (browser) {
await browser.close();
}
console.error(error);
}
return callback(null, result);
};
async function autoScroll(page){
await page.evaluate(async () => {
await new Promise((resolve) => {
let totalHeight = 0;
const distance = 100;
const timer = setInterval(() => {
const scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if(totalHeight >= scrollHeight - window.innerHeight){
clearInterval(timer);
resolve();
}
}, 100);
});
});
}
- 環境変数として
DISCORD_WEBHOOK_URL
とTARGET_URL
を設定する。
環境変数 | 値 |
---|---|
DISCORD_WEBHOOK_URL | [取得したWebHookURL] |
TARGET_URL | https://www.frontier-direct.jp/direct/e/ej-sale/ |
環境変数は、「設定」タブから「環境変数」を選択し、「編集」をして設定する。
Deploy
を実行する。
上記までだと、参照できるライブラリが足りず、実行に失敗する。
そのため、ライブラリをダウンロードし、Lambdaレイヤー
を作成することで動作できるようにする必要がある。
Cloud9を使ってライブラリ郡を用意する
ローカルに環境を作成しても良いが、今記事を書いているマシンには何も入れたくないので Cloud9
を使ってみる。
※ローカルに 環境が入っているのであれば、そちらで構築したほうが早いのでそちらを推奨する。
「Cloud9」サービスを選択する。
「環境を作成」を押す。
作成が完了したら、Cloud9 IDE「開く」を押す。
library.zip
の作成、ターミナルを開いて下記を実行する
axios
とplaywright-core
を入れる。
node -v
v20.15.0
※ランタイムと同じなのでOK
# 作業ディレクトリを作成
mkdir nodejs
cd nodejs
# axiosとplaywright-coreのインストール
npm init -y
npm i axios playwright-core
cd ../
# zipで圧縮する
zip -r library.zip nodejs
chromium.zip
の作成
cd ~/environment
git clone --depth=1 https://github.com/sparticuz/chromium.git && \
cd chromium && \
make chromium.zip
fonts.zip
の作成
cd ~/environment
mkdir fonts && \
cd fonts && \
curl -O https://moji.or.jp/wp-content/ipafont/IPAexfont/IPAexfont00401.zip && \
unzip IPAexfont00401.zip && \
mkdir .fonts && \
cp IPAexfont00401/*.ttf .fonts/ && \
zip -r fonts .fonts
- zipファイルをダウンロードする。
chromium.zip
とfonts.zip
とlibrary.zip
を右クリックし、「Download」を選択する。
上記で、Lambdaレイヤー
で使うライブラリは用意できた。
11. (後始末) Cloud9の環境を削除する。
Lambdaレイヤーの作成
library
レイヤーの作成
fonts
レイヤーの作成
chromium
レイヤーの作成
Lambdaレイヤーの追加
※ chromium
レイヤーの代わりに下記でもいけるはず。
https://github.com/shelfio/chrome-aws-lambda-layer
chromium
の追加
library
の追加
fonts
の追加
Lambda関数の設定変更
puppeteer
やplaywright
でchromium
を使ってキャプチャを取得する実装はメモリを多く使うし、タイムアウト時間もデフォルトだと足りないので、設定を変更する必要がある。
メモリ: 2048MB
タイムアウト: 3分
Lambda関数のテスト
※「Deploy」を実施済みの想定
↑できた!
ただ、スクリーンショットの下の方の表示がおかしいので改善できるかを試してみる。puppeteer-core
→playwright-core
にしてみたが改善せず。
根拠 → https://github.com/puppeteer/puppeteer/issues/1576
EventBridgeの設定
AWSコンソールにて「EventBridge」サービスを選択する。
スケジュールの作成
EventBridgeのナビゲーションペイン「スケジュール」を選択し、「スケジュールを作成」を選択する。
今回は、毎週金曜日の15:00にキャプチャを取るように設定したので、2024/07/05(金)の15:00
に発火できていれば最終動作確認は終了となる。
お疲れ様でした。
参考
Lambdaでpuppeteerを動かす
https://zenn.dev/ispec_inc/articles/lambda-puppeteerAPI Gateway + Lambda + Puppeteer で任意の Web ページのスクリーンショットを撮って S3 に保存する
https://qiita.com/takano-h/items/947392a7b697e123cea4【5分で動く】AWS LambdaでPuppeteer【100%確実に】
https://zenn.dev/aokkey/articles/1fb2e6991f09c2DiscordのWebhookを使ってNode.jsから画像をPOSTする
https://qiita.com/n0bisuke/items/e0d90b7b26de5616db0cPuppeteerでヘッドレス Chromeのフルページスクリーンショットを撮る
https://qiita.com/karusai/items/c60d23f4702d494f56e1AWS Lambda: axios 用の Node.js レイヤーを作成する方法
https://www.linkedin.com/pulse/aws-lambda-how-create-nodejs-layer-axios-michel-bluteau-6jkqeAWS Lambdaでスクリーンショットを撮ってS3に保存
https://qiita.com/akikinyan/items/b775ddb361460a7e9e15SeleniumをLambdaで実行する(快適な)環境を作る
https://qiita.com/K5K/items/1180f73d7e3e74493a40
GitHub
Sparticuz / chromium
https://github.com/Sparticuz/chromiumpuppeteer / puppeteer
https://github.com/puppeteer/puppeteershelfio / chrome-aws-lambda-layer
https://github.com/shelfio/chrome-aws-lambda-layerfullPage screenshot duplicates page (doubles/tripples page length) #1576
https://github.com/puppeteer/puppeteer/issues/1576
おわりに
最初は、Python
で作成しようと思ったがライブラリ関連のエラーの解決がかなり大変そうだったのでNode.js
で作成することにした。puppeteer
を使っては構築できたのだが、不具合なのか実装に足りていないところがあるのかわからないが、キャプチャがおかしい部分があった。
なので、最終的には上記を解決できるplaywright
を使ってみたのだが結局解決しなかった…。
このあたりの内容は後で解決しておきたい…!
Lambda
でレイヤーを使って関数をデプロイするということをやったことがなかったのでかなり躓いたし面倒くさかった。
SAMやServerlessFrameworkを使うとこのあたりかなり楽ができるのかなーと思いながら手を動かしていたので、次回以降はそのあたりを使って作ってみたい。
ただ、Lambda
知る良い機会だったので良しとする。