7
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Puppeteer on Lambda (Node.js 12.x) で日本語ページのキャプチャを取る方法 簡単

Last updated at Posted at 2020-04-26

やりたいこと

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に登録するモジュールの作成

cmd
$ cd modules
$ npm i chrome-aws-lambda puppeteer-core

modulesを丸ごとzipしてmodules.zipを作成

コメント 2020-04-26 233309.png

Lambda Layer作成

このステップはほぼ参考記事のまんまです。

AWSコンソールからLambda Layerを開く

コメント 2020-04-26 233955.png

レイヤー作成(npmモジュール群)

コメント 2020-04-26 233955-2.png

image.png

  • 名前:任意
  • .zipファイルをアップロード:先ほど作成したmodules.zipを選択
  • 互換性のあるランタイム:Node.js 12.x
  • 作成ボタン押下

日本語フォントを登録する

Lambda Layerに登録するための日本語フォントをローカルにダウンロードします。
僕の場合は、以前取得していた日本語フォントファイルが手元にあったので、詳細な手順は割愛します。
IPAのサイトからダウンロードしたり、適当な記事を参考に各自調達してください。

取得したフォントファイルを.fonts配下に格納し、エクスプローラから.fontsフォルダごとzipして.fonts.zipを作成します。
image.png

レイヤー作成(日本語フォント)

再びAWSコンソールからレイヤー作成を行います。
コメント 2020-04-26 233955-2.png

image.png

  • 名前:任意
  • zipファイルをアップロード:先ほど作成した.fonts.zipを選択
  • 互換性のあるランタイム:Node.js 12.x

Lambda関数作成

このステップもほぼ参考記事のまんまです。

AWSコンソールからLambda 関数を開く

image.png

関数作成

image.png
image.png

  • 関数名:任意
  • ランタイム:Node.js 12.x
  • 実行ロール:後で編集するので、作成時は適当に。
  • 「関数の作成」を押下
レイヤー登録

デザイナービューでLayersを選択する
image.png

「レイヤーの追加」ボタンを押下
image.png

レイヤーを追加する
image.png

追加したいレイヤーとバージョンを選択し、「追加」ボタンを押下する。
この手順を繰り返して、先ほど作成した2つのレイヤーを追加する。

index.jsの編集

デザイナービューでLambda関数を選択し、メイン処理を記述する。
image.png

index.js
/* 日本語フォントが入っている.fontsを読み込ませるためにHOMEを設定する */
process.env['HOME'] = "/opt"; // Layerの内容は/optにある

const AWS = require('aws-sdk');
const chromium = require('chrome-aws-lambda');
const puppeteer = require('puppeteer-core');

// パラメータ定義
const SAVE_BUCKET_NAME = 'キャプチャ保存先のバケット名'
const operations = [
  { method: 'goto', args: ['https://www.yahoo.co.jp/'] },
  { method: 'waitFor', args: [1000] }
]

/* ユーティリティ関数 */
// operationsに従ってブラウザ操作。clickやwaitFor時にキャプチャを取得
const execOperations = async function (page, operations, result, jpgBuf) {
  for (const op of operations) {
    console.log(`${op.method} (${op.args[0]} ${op.args.length > 1 ? " ," + op.args[1] : ""})`)
    if (op.method === 'click') {
      jpgBuf.push(await page.screenshot({ fullPage: true, type: 'jpeg' }));
    }
    await page[op.method](...op.args)
    if (op.method === 'waitFor') {
      jpgBuf.push(await page.screenshot({ fullPage: true, type: 'jpeg' }));
    }
  }
}

// S3にキャプチャを保存
const saveJpg = async (jpgBuf) => {
  const s3 = new AWS.S3();
  const now = new Date();
  now.setHours(now.getHours() + 9);
  const nowYMD = now.getFullYear() +
    (now.getMonth() + 1 + '').padStart(2, '0') +
    (now.getDate() + '').padStart(2, '0')
  const nowHMS = (now.getHours() + '').padStart(2, '0') +
    (now.getMinutes() + '').padStart(2, '0') +
    (now.getSeconds() + '').padStart(2, '0');

  for (const idx in jpgBuf) {
    const fileName = 'screenshots/' + nowYMD + '/' + nowHMS + '_' + ('0000' + idx).slice(-4) + '.jpg';
    let s3Param = {
      Bucket: SAVE_BUCKET_NAME,
      Key: fileName,
      Body: jpgBuf[idx]
    };
    await s3.putObject(s3Param).promise();
  }
}

// メイン処理
exports.handler = async (event, context) => {
  let browser = null;
  const result = {}
  const jpgBuf = []

  try {
    // 初期処理
    browser = await puppeteer.launch({
      args: chromium.args.concat(['--lang=ja']),
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath,
      headless: chromium.headless,
    });

    let page = await browser.newPage();
    await page.setExtraHTTPHeaders({
      'Accept-Language': 'ja-JP'
    });

    // ブラウザ操作
    await execOperations(page, operations, result, jpgBuf)

    // 取得したキャプチャをS3に保存
    await saveJpg(jpgBuf)

  } catch (error) {
    return context.fail(error);
  } finally {
    if (browser !== null) {
      await browser.close();
    }
  }

  return context.succeed(result);
};
Lambdaの設定を編集する

メモリとタイムアウトはデフォルトだと少なすぎる/短すぎるので、適当に増やす。
image.png

Lambda関数にアタッチされているIAMロールにS3書き込み権限を付与する

Lambda関数の基本設定の「編集」ボタンを押下してLambdaにアタッチされているIAMロールを確認する。
image.png
「IAMコンソールでxxxxxロールを表示します」のリンクからIAMロールを編集し、S3書き込み権限を付与する。
image.png

S3バケットを作成する

キャプチャ保存用のS3バケットを作成する。
Lambda関数のindex.js の SAVE_BUCKET_NAME に作成したバケット名を設定する。

Lambda関数の実行

Lambda関数の「テスト」を押下する。(初回はテストオブジェクトの定義が必要だが、適当でOK。)
image.png

結果

日本語を含むページのキャプチャが文字化けすることなく取得できました。
image.png

7
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?