やりたいこと
Lambda上でPuppeteerを動かしてキャプチャを取るとき、日本語フォントを正しく表示させる。
(デフォルトではLambdaに日本語フォントが入っていないため、何もしないと日本語がすべて豆腐になってしまうのだ。)
環境
- AWS Lambda
- Node.js 12.x
- ローカル
- Windows10
- Node.js v12.16.2
- npm 6.14.4
やり方(ざっくり)
- Lambda Layerに日本語フォントをzip化してアップロードする。
- Lambda関数にそのLayerを追加する。
- Lambda関数内でLayerのフォントファイルを参照できるようにindex.jsで
process.env['HOME'] = "/opt";
を定義する。
やり方(詳細)
puppeteer_sample
├─ modules # Layerに登録するnpmモジュール群│ ├─ node_modules
│ └─ package-lock.json
├─ .fonts # Layerに登録する日本語フォント置き場│ ├─ ipag.ttf
│ ├─ ipagp.ttf
│ ├─ NotoSansCJKjp-Bold.otf
│ └─ NotoSansCJKjp-Regular.otf
└─ lambda # Lambda本体のコード群└─ index.js
Lambda上でpuppeteerを動作させる
こちらの記事が非常に分かりやすく、参考になりました。
参考記事は、LambdaのランタイムにNode.js 8.10を選択できる時代の記事ですが、Node.js 12.xと読み替えても通用します。
参考記事とほぼ同じですが、僕のやった手順を簡単にメモしておきます。
Lambda Layerに登録するモジュールの作成
$ cd modules
$ npm i chrome-aws-lambda puppeteer-core
modulesを丸ごとzipしてmodules.zipを作成
Lambda Layer作成
このステップはほぼ参考記事のまんまです。
AWSコンソールからLambda Layerを開く
レイヤー作成(npmモジュール群)
- 名前:任意
- .zipファイルをアップロード:先ほど作成したmodules.zipを選択
- 互換性のあるランタイム:Node.js 12.x
- 作成ボタン押下
日本語フォントを登録する
Lambda Layerに登録するための日本語フォントをローカルにダウンロードします。
僕の場合は、以前取得していた日本語フォントファイルが手元にあったので、詳細な手順は割愛します。
IPAのサイトからダウンロードしたり、適当な記事を参考に各自調達してください。
取得したフォントファイルを.fonts配下に格納し、エクスプローラから.fontsフォルダごとzipして.fonts.zipを作成します。
レイヤー作成(日本語フォント)
- 名前:任意
- zipファイルをアップロード:先ほど作成した.fonts.zipを選択
- 互換性のあるランタイム:Node.js 12.x
Lambda関数作成
このステップもほぼ参考記事のまんまです。
AWSコンソールからLambda 関数を開く
関数作成
- 関数名:任意
- ランタイム:Node.js 12.x
- 実行ロール:後で編集するので、作成時は適当に。
- 「関数の作成」を押下
レイヤー登録
追加したいレイヤーとバージョンを選択し、「追加」ボタンを押下する。
この手順を繰り返して、先ほど作成した2つのレイヤーを追加する。
index.jsの編集
デザイナービューでLambda関数を選択し、メイン処理を記述する。
/* 日本語フォントが入っている.fontsを読み込ませるためにHOMEを設定する */process.env['HOME']="/opt";// Layerの内容は/optにあるconstAWS=require('aws-sdk');constchromium=require('chrome-aws-lambda');constpuppeteer=require('puppeteer-core');// パラメータ定義constSAVE_BUCKET_NAME='キャプチャ保存先のバケット名'constoperations=[{method:'goto',args:['https://www.yahoo.co.jp/']},{method:'waitFor',args:[1000]}]/* ユーティリティ関数 */// operationsに従ってブラウザ操作。clickやwaitFor時にキャプチャを取得constexecOperations=asyncfunction(page,operations,result,jpgBuf){for(constopofoperations){console.log(`${op.method} (${op.args[0]}${op.args.length>1?" ,"+op.args[1]:""})`)if(op.method==='click'){jpgBuf.push(awaitpage.screenshot({fullPage:true,type:'jpeg'}));}awaitpage[op.method](...op.args)if(op.method==='waitFor'){jpgBuf.push(awaitpage.screenshot({fullPage:true,type:'jpeg'}));}}}// S3にキャプチャを保存constsaveJpg=async(jpgBuf)=>{consts3=newAWS.S3();constnow=newDate();now.setHours(now.getHours()+9);constnowYMD=now.getFullYear()+(now.getMonth()+1+'').padStart(2,'0')+(now.getDate()+'').padStart(2,'0')constnowHMS=(now.getHours()+'').padStart(2,'0')+(now.getMinutes()+'').padStart(2,'0')+(now.getSeconds()+'').padStart(2,'0');for(constidxinjpgBuf){constfileName='screenshots/'+nowYMD+'/'+nowHMS+'_'+('0000'+idx).slice(-4)+'.jpg';lets3Param={Bucket:SAVE_BUCKET_NAME,Key:fileName,Body:jpgBuf[idx]};awaits3.putObject(s3Param).promise();}}// メイン処理exports.handler=async(event,context)=>{letbrowser=null;constresult={}constjpgBuf=[]try{// 初期処理browser=awaitpuppeteer.launch({args:chromium.args.concat(['--lang=ja']),defaultViewport:chromium.defaultViewport,executablePath:awaitchromium.executablePath,headless:chromium.headless,});letpage=awaitbrowser.newPage();awaitpage.setExtraHTTPHeaders({'Accept-Language':'ja-JP'});// ブラウザ操作awaitexecOperations(page,operations,result,jpgBuf)// 取得したキャプチャをS3に保存awaitsaveJpg(jpgBuf)}catch(error){returncontext.fail(error);}finally{if(browser!==null){awaitbrowser.close();}}returncontext.succeed(result);};
Lambdaの設定を編集する
メモリとタイムアウトはデフォルトだと少なすぎる/短すぎるので、適当に増やす。
Lambda関数にアタッチされているIAMロールにS3書き込み権限を付与する
Lambda関数の基本設定の「編集」ボタンを押下してLambdaにアタッチされているIAMロールを確認する。
「IAMコンソールでxxxxxロールを表示します」のリンクからIAMロールを編集し、S3書き込み権限を付与する。
S3バケットを作成する
キャプチャ保存用のS3バケットを作成する。
Lambda関数のindex.js の SAVE_BUCKET_NAME に作成したバケット名を設定する。
Lambda関数の実行
Lambda関数の「テスト」を押下する。(初回はテストオブジェクトの定義が必要だが、適当でOK。)