Web クローラーを AWS Lambda で動かした
こんにちは。 今日は年末年始に Web クローラーを作ったので、備忘録としてまとめます。
動機
妻の仕事の作業として、毎週特定の曜日に Web ページへ手入力をしている作業があり、面倒と言っていました。
退屈な作業はコンピューターにやらせてしまえ、ということで Web クローラー作ろうと思ったわけです。 (あと TypeScript の勉強にもなるとも)
構成
以下のような構成で作りました。
- Web クローラー : puppeteer
- 実装 : TypeScript
- 実行環境 : AWS Lambda
- データストア : Amazon DynamoDB
- テスト : Jest
- インフラの定義 : Serverless Framework, (Terraform)
- CI/CD パイプライン : Github Actions
puppeteer
puppeteer はプログラムから API で Chrome ブラウザを制御できる Node.js のライブラリです。 JavaScript で作られており、同期処理を使って以下のように簡単に Web クローラーを実装できます。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch(); // ブラウザオブジェクト作成
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'example.png' }); // スクショ保存
await browser.close();
})();
もちろん、TypeScript でも書けます。
また、Chromium のヘッドレスブラウザ(GUI を使わず起動できるブラウザ)を内蔵しています。
AWS Lambda
AWS Lambda(Lambda) は AWS が提供している、サーバレスでアプリケーションコードを実行できるコンピューティングサービスです。
イベント駆動型のサービスとして使うことが一般的です。
今回は EventBridge も使って、特定の時間にイベントを発生させて Lambda を起動し、Web クローラーを実行させます。
puppeteer on AWS Lambda
Lambda は保存できるコードのサイズに圧縮時 50 MB, 非圧縮時 250 MB までの制限があります。[参考]
puppeteer はそのままだと、内蔵された Chromium ヘッドレスブラウザの容量が大きいせいでこの制限を超えてしまいます。 つまり、puppeteer のサイズを削減する必要があります。
そこで、puppeteer の Chromium ブラウザを Lambda 用に最適化して容量を削減した、chrome-aws-lambda を使います。 これにより、Lambda のコードのサイズ制限を回避できます。
ちなみに、先程のコードは下記のように置換する必要があります。
const chromium = require('chrome-aws-lambda');
(async () => {
const browser = await chromium.puppeteer.launch(); // ブラウザオブジェクト作成
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'example.png' }); // スクショ保存
await browser.close();
})();
Amazon DynamoDB
Amazon DynamoDB(DynamoDB) は AWS マネージドの NoSQL データベースサービスです。
今回、Web サイトから取得したデータの内容によって、Web クローラーを起動時に行う処理が変わるため、データを保存する必要がありました。
簡単な構造化データを保存するだけで、AWS でインフラを構築しているということもあり DynamoDB を採用しました。
Web クローラーの実装では aws-sdk を使って DynamoDB とデータをやり取りしています。
(なお、DynamoDB は色々な事情により Terraform で構築しましたが、今回は割愛します。)
Serverless Framework
Lambda と EventBridge をデプロイさせる役には Serverless Framework を使いました。
Serverless Framework はサーバレスに特化したインフラストラクチャとアプリケーションをデプロイするためのフレームワークです。 Serverless Framework では YAML ファイルにインフラを定義してデプロイをします。以下は Lambda + EventBridge の定義例になります。
service: lambda
plugins:
- serverless-plugin-typescript # アプリケーションのコードに TypeScript を使うため
frameworkVersion: "2"
provider:
name: aws
region: ap-northeast-1
runtime: nodejs14.x
functions:
lambda:
handler: lambdaHandler.handler # Lambda のコードを実行させるメソッド
role: arn:aws:iam::XXXXXXXXXXXX:role/ExampleLambdaExecutionRole
events:
- schedule:
enabled: true
rate: cron(0 0 ? * MON-THU *)
Github Actions
CI/CD パイプラインには慣れていた Github Actions を使いました。 Github Actions はテストと Serverless Framework によるデプロイを実行する役になります。 なお、テストには JavaScript のテストライブラリ Jest を使っています。(今回割愛します)
Github Actions も YAML ファイルにデプロイするワークフローを定義します。 以下では、Github リポジトリからコードをチェックアウトし、Serverless Framework を使ってデプロイする例になります。
on:
push:
branches:
- main
jobs:
apply:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
strategy:
matrix:
node-version: [14.x]
steps:
- name: Check out repository # リポジトリのチェックアウト
uses: actions/checkout@v2
- name: Configure AWS # Serverless Framework からでプロイするため AWS Credential を設定
uses: aws-actions/configure-aws-credentials@master
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
- name: serverless deploy # https://github.com/serverless/github-action を使います。コードの記述量が減って便利です。
uses: serverless/github-action@master
with:
args: deploy
まとめ
今回、puppeteer, Lambda, DynamoDB を使った Web クローラーを作った話をしました。 puppeteer 初心者でしたが、空いた時間に 3 日くらいで実装できました。 もし、Web ページへ煩雑な作業があるとき、作ってみてはいかがでしょうか。
Lambda や DynamoDB は無料利用枠もあるので、簡単な Web クローラーなら無料で使えると思います(私も無料枠に収まっています)[Lambda 料金][DynamoDB 料金]