概要
- lambdaでpuppeteerを動かす
- chromeはlayerを利用
- chrome-aws-lambda を layerで追加
- lambda functionはSAMを利用してdeploy
- (おまけ) API gatewayで公開
前提
- aws cliでawsにフルにアクセスできる状況になっている
コード
参考
以下の3つの記事を合体した感じです
- AWS Lambda Layers上でHeadless Chromeを動かすいくつかの方法
- AWS LambdaでPuppeteerを動かす
- Lambda Layers をnode.js(SAM)で試してみる
手順
chromeとpuppeterのlayerを追加
AWS LambdaでPuppeteerを動かす この記事の Layerのアップロード までやる
SAMテンプレート作成
- Lambda Layers をnode.js(SAM)で試してみる を参考。
-
Layers
の箇所は上記で作成したLambdaのLayersのARNをコピーして貼り付けましょう
bucket
は後述のlabmdaと同じものです。ご自分のbucketを指定してください。(なければ作りましょう!)
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: screenshot2s3
Resources:
Screenshot2S3Function:
Type: AWS::Serverless::Function
Properties:
Policies:
- S3CrudPolicy:
BucketName: screenshot2s3
FunctionName: screenshot2s3
CodeUri: src
Handler: index.handler
MemorySize: 1600
Runtime: nodejs8.10
Timeout: 30
Events:
Get:
Type: Api
Properties:
Path: /
Method: get
Layers:
- "arn:aws:lambda:ap-northeast-1:あなたの:layer:myPup:1"
Events
の箇所はおまけで使うAPI Gatewayでアクセスするためです。
Lambda Function追加
-
functions/pupfunc/index.js
にLambda関数を書きます。 - usage をもとに、screenshotをキャプチャするコードに書き換えました。
- 日本語フォントが使えるように下記の記述を追加しました
await chromium.font('https://raw.githack.com/googlei18n/noto-cjk/master/NotoSansCJKhk-Regular.otf');
https://github.com/googlei18n/noto-cjkから選んで
https://raw.githack.com
に書いて chromium.font(ここ)
に貼るだけ。
bucket
はご自分のbucketを指定してください。
const chromium = require('chrome-aws-lambda');
const puppeteer = require('puppeteer-core');
const aws = require('aws-sdk');
const s3 = new aws.S3();
const bucket = 'screenshot2s3';
const crypto = require('crypto');
exports.handler = async (event, context) => {
const query = event.queryStringParameters;
const displaySize = {
pc: {width: 1024, height: 1024},
iPhoneX: {width: 375, height: 812},
iPad: {width: 768, height: 1024}
};
const devise = displaySize['pc'];
const url = query.url;
let browser = null;
await chromium.font('https://rawcdn.githack.com/googlei18n/noto-cjk/cf29231ab8029678af4bbc1a9480e2b296a5b2d3/NotoSansCJKhk-Regular.otf');
try {
browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath,
headless: chromium.headless,
});
let page = await browser.newPage();
await page.goto(url);
page.setViewport({width: devise.width, height: devise.height})
const screenshot = await page.screenshot();
const filename = randomFilename(screenshot);
let params = {Bucket: bucket, Key: filename, Body: screenshot};
await s3.putObject(params).promise();
const download_params = {Bucket: bucket, Key: filename, Expires: 86400};
const download_url = s3.getSignedUrl('getObject', download_params);
return ({
"statusCode": 200,
"body": JSON.stringify({download_url: download_url})
})
} catch (error) {
return context.fail(error);
} finally {
if (browser !== null) {
await browser.close();
}
}
};
const randomFilename = (data) => {
const sha256 = crypto.createHash('sha256');
sha256.update(data);
return sha256.digest('hex') + '.png'
}
const query = event.queryStringParameters;
の箇所は、後述のAPI gatewayでアクセスするためです。 lambdaだけであれば、 event.url
でシンプルに受け取ってOK。
参考
- https://confrage.jp/aws-s3%E3%81%AE%E6%9C%9F%E9%99%90%E4%BB%98%E3%81%8Durl%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%80%81s3%E3%81%AB%E3%81%82%E3%82%8B%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3/
- https://aws.amazon.com/jp/premiumsupport/knowledge-center/malformed-502-api-gateway/
- https://dev.classmethod.jp/server-side/serverless/20171203-updates-about-aws-serverless-application-model/
- https://sasaplus1.hatenadiary.com/entry/20120211/1328964435
- https://qiita.com/naoki_koreeda/items/c2a32198c86e8d9a5bb6
- https://qiita.com/Quantum/items/91ad6b6b788bf4051055
- https://confrage.jp/amazon-api-gateway%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9%E3%81%A8%E3%82%AF%E3%82%A8%E3%83%AA%E6%96%87%E5%AD%97%E5%88%97%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E6%B8%A1%E3%81%97%E6%96%B9/
- https://dev.classmethod.jp/server-side/serverless/20171203-updates-about-aws-serverless-application-model/
deploy
--s3-bucket
の指定はご自分のバケットつかってください。
SAMのデプロイは一度S3にアップロードが必要です。なければ、バケットはここをみてつくってきださい。コマンドライン一行ですよー。
sam package \
--template-file template.yaml \
--output-template-file packaged.yaml \
--s3-bucket つくったバケット
sam deploy \
--template-file packaged.yaml \
--stack-name sam-app \
--capabilities CAPABILITY_IAM
確認
Lambdaを開いて先ほど作った関数がデプロイされていて、layerが割り当てられていることを確認して下さい。
テストのデータは下記のようなデータを指定してください。 "https://xxxxxx.co.jp"
は、なんでもいいです。
{
"queryStringParameters": {
"url": "https://xxxxxx.co.jp"
}
}
lambdaのテスト実行して、ご自分のバケットに、xxxx.pngがアップロードされていれば成功です。例はyahooのサイトです。
以上。
おまけ
ドメインとってAPI gatewayで公開しました。
使い方
-
https://webscreenshot.work
に url パラメータにスクショ撮りたいアドレスを書いて下さい。 - リクエストヘッダーに
x-api-key: 7eXZ6duNSJ754gLUt5ENX3mSXgnv3UqX7QYsB5ZF
をいれてください。 - 出力されるjsonの
download_url
のvalueにアクセスするとダウンロードできます。 - APIキー毎に回数を制限しているので、エラーが発生したらそれは回数制限に達したからです。翌日アクセスお願いします。
curlでかくとこんな感じ。
curl -v https://webscreenshot.work/?url=https://yahoo.co.jp -H "x-api-key: 7eXZ6duNSJ754gLUt5ENX3mSXgnv3UqX7QYsB5ZF"
省略
* Connection #0 to host webscreenshot.work left intact
{"download_url":"https://screenshot2s3.s3.ap-northeast-1.amazonaws.com/b35d3bda10ac3714eb9b03b19e361cc8d801ddf61eb165fd7b489988276d2d06.png?AWSAccessKeyId=ASIAREY2BQFLWVZRL7GP&Expires=1555342761&Signature=3MkWy5EjqOchNCzJsj1qOvvan1w%3D&x-amz-security-token=FQoGZXIvYXdzECEaDLlrEieFh6KQaAnxIiLmAamAhWMbYrvRuLbJcQPyL7p%2BCx4CYVG%2BkLrRliDzXpxnmDepaxumy7kWPIyXLmqpAk6hbg2lbQhUDciPs4bgnrPr%2FjZm%2FsxtVvtOsnvP3nQRC%2FswlLsodcUR%2FicANlR2Po4yIUriJ6ZxIhj19AVZo5ZfaVdhNqF%2B0uUPRwqDZNucCEcxjuRBNDlLU%2FYk2WS%2BZatRq1geZJNBq%2Ffc%2FM5mcad%2F%2BGHE1c7ipEhVmF1FhIvyUR31GK%2F3dyhkS3iXut8lWKrxrVW0TmGH9xVmHrDp8821lg7P%2Bd6F%2FBKfT4TWQDqahk09ARnoKI6ozeUF"}%
手順
SAMでdeployするとすでにAPI gatewayにリソースが追加されていますので、それをベースに使います。
APIの必要性を trueにします
- APIをProdでデプロイします
- API GatewayをカスタムドメインでHTTPS化する を参考に作業します。私の場合は value-domainでドメインを用意していたので、 こちらも参考にしました。
- お金かかるのでAPIのアクセス制限します
以上