AWS Lambdaでスクリーンショットを撮ってS3に保存

# はじめに

この記事は、[mohikanz Advent Calendar 2019](https://adventar.org/calendars/3884)の11日目の記事です。
去年は、[マイコンネタ](https://qiita.com/akikinyan/items/f5a27f6f7e4ea584f467)でした。
今年は、AWSで日頃便利に使わせてもらっている技術ネタについて書きます。

あるプロジェクトで、Webサイトを定期的にキャプチャして、S3に保存しておきたいなぁと言う事があり、それならばサーバレスで挑戦してみようと言うことになり、それをやってみたときの記事になります。
Lambdaは便利ですよね。何よりお安いですし。

# 必要な知識

- Python
- AWS Lambda
- Selenium

# 事前準備

headless chromeを使いますので、いくつか事前準備が必要です。

## Lambda Layers

Lambdaのファイルサイズ上限に引っかかるので、Lambda Layersにheadless chromeを用意します。
GitHubに置いておきましたので、ご自由にお使いください。
Lambda Layersは、マネジメントコンソールからアップロードが出来ます。

https://github.com/akikinyan/headless_chrome_python360

## 日本語フォント

IPAexフォントがあると文字化けしませんので、あらかじめダウンロードしておいてください。
```./fonts```ディレクトリに保存しておきます。
Lambda Layersに含めると50MBを超えてしまうので、Lambdaの方に追加します。

https://ipafont.ipa.go.jp/old/ipaexfont/download.html

- 2書体パック(IPAex明朝(Ver.002.01)、IPAexゴシック(Ver.002.01))


# Lambdaの作成

Serverless Frameworkを使って構築します。

```console
$ sls create --template aws-python3 --path headless-chrome
```

```yaml:serverless.yml
service: headless-chrome

provider:
  name: aws
  runtime: python3.6
  memorySize: 1600
  timeout: 120
  stage: ${opt:stage, 'dev'}
  region: ap-northeast-1
  # 権限設定
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "s3:GetObject"
        - "s3:PutObject"
      Resource: "arn:aws:s3:::headless-chrome-${self:provider.stage}/*"
functions:
 chrome: 
    handler: chrome.lambda_handler
    description: Headless Chrome
    environment:
      TZ: Asia/Tokyo
      STAGE: ${self:provider.stage}
      S3BUCKET: headless-chrome-${self:provider.stage}
    package:
      # フォントを追加する
      include: 
        - '.fonts/**' 
    layers: 
      - arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:layer:headless_chrome_python360:1
```

Lambdaのソースコード

```python:chrome.py
from selenium import webdriver
import boto3
import json
import os
import time


os.environ['HOME'] = '/var/task'


def lambda_handler(event, context):
    options = webdriver.ChromeOptions()
    options.add_argument("--headless")
    options.add_argument("--disable-gpu")
    # options.add_argument("--window-size=3408x2156")
    options.add_argument("--window-size=1704x1078")
    options.add_argument("--disable-application-cache")
    options.add_argument("--disable-infobars")
    options.add_argument("--no-sandbox")
    options.add_argument("--hide-scrollbars")
    options.add_argument("--enable-logging")
    options.add_argument("--log-level=0")
    options.add_argument("--v=99")
    options.add_argument("--single-process")
    options.add_argument("--ignore-certificate-errors")
    options.add_argument("--homedir=/tmp")

    options.binary_location = "/opt/bin/headless-chromium"

    bucket = os.environ.get("S3BUCKET", "")
    s3 = boto3.client('s3')
    driver = webdriver.Chrome(
        "/opt/bin/chromedriver", chrome_options=options)
    driver.get("https://www.google.co.jp")

    time.sleep(5)
    driver.save_screenshot("/tmp/shot.png")
    driver.close()

    s3.upload_file(Filename="/tmp/shot.png",
                   Bucket=bucket,
                   Key="hoge.png")
    return "ok"
```

# 実行してみた

```
$ sls deploy -v
$ sls invoke -f chrome
```

こんな感じになりました。

![hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150734/889f2367-f6d5-f36e-0c85-1f3315a20db0.png)

# さいごに

Seleniumを使いこなせる人は、CI/CDに組み込んだりしてみると、リリースの動作確認まで出来ちゃうので、夢が広がりますね。
chromeはfileプロトコルも使えるので、ローカルに置いたファイル(Lambdaが動いてる実行環境)を使うこともできます。