はじめに
Puppeteerはプログラムからブラウザ(Chrome or Chromium)を操作して利用できるNode.js用のライブラリです。Puppeteerを使用することで、
などが簡単に行えます。
Puppeteerは裏側でブラウザを起動するため、CPUやメモリなどのリソースを多く使用します。PuppeteerをAWS Lambda上で利用することで、同時実行の際のCPU負荷やメモリ使用量を気にすることなく、スケールする環境で利用できるようになります。
PuppeteerをLambdaを利用するための記事はたくさん存在しますが、古いものだと色々と手順が面倒だったり、動かなかったりするものもあります。
ここでは、2019年9月時点で、PuppeteerをLambda上でTypescriptから使うための、最短な手順をご紹介します。
事前準備
以下を事前に行っておいてください。
- Node.jsのインストール
- AWS CLIのインストールと設定
- Serverless Frameworkのインストール
プロジェクトのセットアップ
Serverless Frameworkのテンプレートから、Typescriptベースのプロジェクトを作成します。 以下のコマンドを実行します。
mkdir example cd example sls create --template aws-nodejs-typescript npm install
まずは1度デプロイしてみる
まずこの時点でデプロイして動作させて、ビルドやデプロイができることを確認しておきます。これにより、この後で追加・修正によって発生した問題の切り分けを行いやすくなります。
以下のコマンドでLambdaにデプロイします。
sls deploy
... api keys: None endpoints: GET - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello functions: hello: example-dev-hello layers: None
上記のログがコンソールに出力されます。ログ中のendpointsのURLにブラウザでアクセスしてみましょう。(エラーが発生した場合は、事前準備を確認してみてください)
{ "message": "Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!", "input": { "resource": "/hello", "path": "/hello", ...
ブラウザに上記のようなJSONが出力されれば、デプロイは成功です。
Puppeteerのセットアップ
次にPuppeteerを利用するための設定を追加していきます。
puppeteer-core、chrome-aws-lambdaのモジュールをインストールします。また、Puppeteerの型をTypescriptで認識するために @types/puppeteerを、次の作業のためにwebpack-node-externalsもインストールしておきます。
以下のコマンドでインストールできます。
npm i puppeteer-core npm i chrome-aws-lambda npm i @types/puppeteer -D npm i webpack-node-externals -D
次に、Puppeteerの対応のために、webpack.config.jsを修正します。
webpack.config.jsの先頭で「webpack-node-externals」を読み込み、設定に「externals: [nodeExternals()]」を追加します。これにより、node_modules配下を1つのファイルに結合しない設定になります。
webpack.config.js
const path = require('path'); const slsw = require('serverless-webpack'); const nodeExternals = require('webpack-node-externals'); // 追加 ... module.exports = { mode: slsw.lib.webpack.isLocal ? 'development' : 'production', ... target: 'node', externals: [nodeExternals()], // 追加 module: { ... ...
serverless.ymlを修正する
serverless.ymlを3箇所修正します。
1. NodeJSのバージョンを8.10に
Lambdaで使用するnodeのバージョンを「10」から「8.10」に変更します。これは「chrome-aws-lambda」モジュールが「nodejs8.10」環境にしか対応していないためです。
2. メモリサイズとタイムアウトをデフォルト値から変更する
PuppeteerをLambdaで使用する場合、メモリはある程度大きなものにしておく必要があります。ここでは1024にします。また、タイムアウト値も、デフォルトの6秒では短すぎるため、30秒に変更します。
3. webpackIncludeModulesを有効にする
また、nodeExternalsに関連したカスタム設定として「webpackIncludeModules: true」を追加して、モジュールがパッケージに含まれるようにします。
変更後のserverless.ymlは以下のようになります。
serverless.yml
... provider: name: aws runtime: nodejs8.10 # 変更 functions: hello: handler: handler.hello memorySize: 1024 # 追記 timeout: 30 # 追記 events: - http: method: get path: hello # 以下追加 custom: webpackIncludeModules: true
コードの修正
最後にhandler.tsをPuppeteerを使用したコードに修正します。ここでは
- 対象のURLをパラメーターで受け取り
- 対象URLにPuppeteerを使ってアクセスし
- ページのタイトルを取得して返す
という処理を追加します。
handler.ts
import 'source-map-support/register'; import { APIGatewayProxyHandler } from 'aws-lambda'; import * as chromium from 'chrome-aws-lambda'; import { Browser } from 'puppeteer'; export const hello: APIGatewayProxyHandler = async (event, _context) => { const { url } = event.queryStringParameters; let browser: Browser = null; try { browser = await chromium.puppeteer.launch({ args: chromium.args, defaultViewport: chromium.defaultViewport, executablePath: await chromium.executablePath, headless: chromium.headless, }); const page = await browser.newPage(); await page.goto(url); const title = await page.title(); return { statusCode: 200, body: JSON.stringify({ title, url, }, null, 2), }; } finally { if (browser !== null) { await browser.close(); } } };
再度デプロイする
再度sls deploy
コマンドでデプロイします。エンドポイントに対して、
https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello?url=https%3A%2F%2Fadlive.asia%2F
のようにurlパラメータ付きでアクセスします。すると以下のようにタイトルが取得できているのが確認できました。
{ "title": "営業専門家のWEBマーケティング|アドリヴ株式会社‐ADlive. Inc.", "url": "https://adlive.asia/" }
今回のソースコードはGitHubにあげていますので参考にされてください。
デプロイしたアプリを削除しておく
不要になったらsls remove
ですべてのリソースを削除しておきましょう。
終わりに
手軽にスケールするPuppeteer環境が使えるのって素晴らしいですね。今回のサンプルは誰でも外からアクセスできるので、実運用の際はVPC Endpointなどを利用して、アクセス制限をかけて利用しましょう。