1
0

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.

Puppteerでキャプチャ画像を取得 part1

Last updated at Posted at 2020-05-05

概要

エンジニアもどき2年目に入りました。色々あって以下を行うLambda関数を作成することになりました。
色々学びがあったので備忘録がわり兼アウトプットのために書くことにしました。

  1. 特定のページのキャプチャ取得
  2. 取得したキャプチャをS3に保存
  3. S3に保存したURLをDyanmoに記録

part1では1. 特定のページのキャプチャ取得の実装とデプロイまで行います。
part1での実装内容はこちら

前提

以下の内容が出てきます。インストールなどの準備は完了している前提です。

  • nodejs
  • ServerlessFramework
  • yarn
  • webpack
  • iamのユーザ作成済み

実装作業

1. 準備

フォルダとテンプレートの作成をします。

mkdir puppeteer-capture
cd puppeteer-capture
serverless create --template aws-nodejs

作成されたテンプレートは次のような構成になります。
image.png

また、webpackを使用する(modeule構文などesnextで実装する)ため次のパッケージも追加します。

yarn add -D webpack webpack-cli
yarn add -D @babel/core @babel/preset-env babel-loader
yarn add -D serverless-webpack

webpackの設定を書きます。

jsconfig.json
{
  "compilerOptions": {
    "module": "esnext",
    "baseUrl": ".",
  },
  "exclude": ["node_modules"]
}
webpack.config.js
const path = require('path');
const slsw = require('serverless-webpack');

module.exports = {
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  entry: slsw.lib.entries,
  devtool: slsw.lib.webpack.isLocal
    ? 'cheap-module-eval-source-map'
    : 'source-map',
  output: {
    libraryTarget: 'commonjs',
    filename: '[name].js',
    path: path.join(__dirname, '.webpack'),
  },
  target: 'node',
  module: {
    rules: [
      {
        test: /\.js$/,
        enforce: 'pre',
        exclude: /node_modules/,
        include: __dirname,
        use: [
          {
            loader: 'babel-loader',
          },
        ],
      },
    ],
  },
};

以上で事前準備は完了です。

2. キャプチャを取得する関数の実装

キャプチャを取得してローカルに保存する関数を作成します。

2-1. ライブラリの追加

今回はブラウザを操作するpuppeteer-coreとAWS上でchromium(ブラウザ)を動かすchrome-aws-lambdaを使用します。また、ローカルでの動作確認用にpuppeteerも追加しています。

# ライブラリの追加
yarn add puppeteer-core chrome-aws-lambda
yarn add -D puppeteer

事前準備で追加したwebpackの設定を除き、package.jsonに以下の3つのライブラリが追加されていれば完了です。

package.json
{
  "devDependencies": {
    // ・・・ 省略 ・・・
    "puppeteer": "^3.0.2",
    // ・・・ 省略 ・・・
  },
  "dependencies": {
    "chrome-aws-lambda": "^2.1.1",
    "puppeteer-core": "^3.0.2"
  }
}

2-2. 実装

handler.jsにキャプチャの取得/ローカルへの保存を行う関数を実装します。

handler.js
import { args, defaultViewport, executablePath, headless, puppeteer } from 'chrome-aws-lambda';
import { writeFileSync } from 'fs';

const getCapture = async (url) => {
  // ブラウザの起動
  let browser = null;
  try {
    browser = await puppeteer.launch({
      args,
      defaultViewport,
      executablePath: await executablePath,
      headless,
    });

    // ページに移動
    const page = await browser.newPage();
    await page.goto(url);

    // キャプチャの取得(フルページ、jpegを指定)
    return await page.screenshot({ fullPage: true, type: 'jpeg' });
  } catch (error) {
    console.log(error);
    return null;
  } finally {
    if (browser !== null) {
      await browser.close();
    }
  }
};

export const captureFunction = async event => {
  const url = event.url || 'https://google.com/';

  // キャプチャ取得
  const jpgBuf = await getCapture(url);
  if (!jpgBuf) {
    return { statusCode: 500, body: 'キャプチャの取得に失敗しました.' };
  }

  // ファイルに書き出し
  writeFileSync('/tmp/hoge.jpg', jpgBuf);

  return { statusCode: 200, body: 'キャプチャの取得に成功しました。' };

};

2-3. 動作の確認

まずはserverless.ymlを以下のように編集します。
regionやprofileの項目は各自の環境に合わせて記述してください。

serverless.yml
service: puppeteer-capture
provider:
  name: aws
  runtime: nodejs12.x
  region: ap-northeast-1
  profile: private
  stage: dev
# 使用するプラグイン
plugins:
  - serverless-webpack
# 関数の設定
functions:
  captureFunction:
    handler: handler.captureFunction

以下を実行して動作の確認を行います。

# ローカルでの動作確認
sls invoke local --function hello -c ./serverless.yml

一瞬ブラウザが立ち上がり/tmphoge.jpgという名前でキャプチャ画像が取得できていればOKです。
任意のページのキャプチャを取得する場合は、以下のように実行します。

# 任意のページのキャプチャを取得
sls invoke local --function captureFunction --data '{"url":"https://qiita.com/"}' -c ./serverless.yml

デプロイ

作成したLambda関数をAWS上にデプロイします。

1. 準備

chrome-aws-lambda込みでlambdaにデプロイすると容量が結構大きくなってしまうためパッケージはLambda Layerに入れておき、そこから使うことにします。

まずは、Layersに配置するものを格納しておくディレクトリを作成し、パッケージを追加します。
ドキュメントによると[任意の名前]/nodejs/node_modulesにパッケージを追加する必要があるようです。

# ディレクトリの作成
mkdir layers/nodejs
cd layers/nodejs
# パッケージの追加
yarn init -y
yarn add chrome-aws-lambda puppeteer-core

あわせて、webpack.config.jsに外部依存の設定を追加します。

webpack.config.js
  // ・・・ 省略 ・・・
  target: 'node',
  // 追加
  externals: ['chrome-aws-lambda'],
  // ・・・ 省略 ・・・
};

2. Layersの設定

Layersに格納するパッケージを格納しているパスと関数からの参照を行うための設定をserverless.ymlに記述します。
コンソールからもLayersの設定はできますが、今回は設定に関しては全てコンソールを使わずに行ないます。
Layer設定のname項目によりlambdaだけでなくlayerもstageでの切り分けができます。

serverless.yml
service: puppeteer-capture
provider:
  # ・・・ 省略 ・・・
  # 追加1 : Layer名などの環境変数
  environment:
    STAGE: ${self:provider.stage}
    PREFIX: ${self:service}-${self:provider.stage}
    CAPTURE_LAYER: ${self:provider.environment.PREFIX}-capture-layer
# ・・・ 省略 ・・・
# 関数の設定
functions:
  captureFunction:
    handler: handler.captureFunction
    # 追加2 : Layerの参照
    layers:
      - { Ref: CaptureLayerLambdaLayer }
# 追加3 : Layerの設定
layers:
  CaptureLayer:
    path: layers
    name: ${self:provider.environment.CAPTURE_LAYER}

3. デプロイと動作確認

以下のコマンドでデプロイすると画像のような結果が得られます。

# デプロイ
serverless deploy       

image.png

後はデプロイした関数の動作を確認するだけです。

# デプロイした関数の実行
sls invoke --function captureFunction -c ./serverless.yml   
sls invoke --function captureFunction --data '{"url":"https://qiita.com/"}' -c ./serverless.yml

以下のようにレスポンスが返ってきていれば完了です。

image.png

lambdaは実行後、使用されたすべてのリソース(に格納されているすべてのファイルを含む)が破棄されてしまうので、/tmpではなくS3などに保存する必要があります。そこで次回は2. 取得したキャプチャをS3に保存するように修正します。

おわりに

特に大変だったのは次の3点でした。

AWS Serverless Application Modelも気になるのでいずれはそちらも使ってみたいなぁ。

参考

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?