PythonとSeleniumでスクショをS3にアップロードするLambdaを作る

はじめに

基本的にはこちらを参考にしています。

Lambda関数作成

serverless-chroniumダウンロード

Lambdaの様なサーバレスで動かすヘッドレスなブラウザです。

$ mkdir -p bin/
$ curl -SL https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-57/stable-headless-chromium-amazonlinux-2.zip > headless-chromium.zip
$ unzip headless-chromium.zip -d bin/
$ rm headless-chromium.zip

chromedriverダウンロード

$ curl -SL https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip > chromedriver.zip
$ unzip chromedriver.zip -d bin/
$ rm chromedriver.zip

seleniumダウンロード

Amazon Linux2で動くSeleniumをダウンロードするために、Dockerコンテナを作成します。(Macなどで直接pip installするとMac用のSeleniumをダウンロードしてしまうから)

下記はそのためのDockerfileです。

FROM lambci/lambda:build-python3.6

ENV AWS_DEFAULT_REGION ap-northeast-1
ENV APP_DIR /var/task

ADD . .

CMD pip install -r requirements.txt -t $APP_DIR && \
  zip -9 deploy_package.zip lambda_function.py && \
  zip -r9 deploy_package.zip *

requirements.txtを作成する。

selenium

スクショを取得してS3にアップロードするソースコード

from selenium import webdriver
import boto3

def lambda_handler(event, context):
    options = webdriver.ChromeOptions()

    # のちほどダウンロードするバイナリを指定
    options.binary_location = "./bin/headless-chromium"

    # headlessで動かすために必要なオプション
    options.add_argument("--headless")
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1280x1696")
    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("--single-process")
    options.add_argument("--ignore-certificate-errors")
    options.add_argument("--homedir=/tmp")

    # set driver and url
    driver = webdriver.Chrome(
        "./bin/chromedriver",
        chrome_options=options)
    url = 'https://www.google.com/search?q=%E6%9D%B1%E4%BA%AC+%E5%A4%A9%E6%B0%97'
    driver.get(url)
    title = driver.title

    # get width and height of the page
    w = driver.execute_script("return document.body.scrollWidth;")
    # set window size
    driver.set_window_size(w,600)
    # Get Screen Shot
    weather_capture = driver.save_screenshot('/tmp/weather.png')
    driver.close()

    # s3
    s3 = boto3.client('s3')
    bucket = '[バケット名]'
    s3.upload_file(Filename="/tmp/weather.png",
                   Bucket=bucket,
                   Key="weather.png")

    return title

下記のコマンドを実行するとLambdaにアップロードするdeploy_package.zipが作成されます。

$ docker build -t lambda_headless_chrome .
$ docker run --rm -v "${PWD}":/var/task lambda_headless_chrome

AWS側の設定

Lambdaリソース設定

aws cliでもGUIでもいいのでLambdaを作成して下さい。

タイムアウト時間は5分、メモリは512MB程度にしないとそもそも動かない可能性があります。

IAMロール設定

Lambdaには実行可能ポリシーに加えて、S3へアップロードしたいので「AmazonS3FullAccess」ポリシーもアタッチして下さい。

S3バケット作成

S3バケットを作成して下さい。また作成したバケット名をlambda_function.pyに記述します。

fonts

このままだと検索結果が文字化けしたままスクリンショットを保存してしまいます。そのため日本語対応したフォントを追加します。ここから「Noto Sans CJK JP」と「Noto Serif CJK JP」の2つをダウンロードして下さい。

以下のディレクトリ階層となるzipを作成します。

$ tree .fonts                                                                                                                                                                                                                      [master]
.fonts
├── NotoSansCJKjp-Regular.otf
└── NotoSerifCJKjp-Regular.otf
$ zip -r fonts.zip .fonts

これをLambda関数に含めるとファイルサイズが大きくなるのでLambdaレイヤーに配置します。またLambda関数が.fontsディレクトリを認識するために次の環境変数を追加します。

HOME/opt/

テストを実行すると「東京 天気」の検索結果のスクリーンショットがS3に保存されるはずです。

$ aws s3 cp s3://[バケット名]/weather.png ./
$ open weather.png

ルール作成

CloudWatchのルール追加から定期実行をするイベントを作成します。スケジュール方式でCRONを設定し、ターゲットに先程作成したLambdaを登録します。

S3から画像を取得してSlackに送信する

S3にアップロードするLambdaを作ったので、次はS3からSlackに画像を送信するスクリプトを作成したいと思います。Slack APIの詳細についてはこちらをご確認下さい。

まずは先程と同様にLambda関数を作成して下さい。次にコードを書いていきますが、先程のディレクトリをコピーして作成していきます。requirements.txtとlambda_function.pyを次の様に変更します。

requests
import requests
import boto3
import base64

def lambda_handler(event, context):
    s3 = boto3.client('s3')
    bucket = '[バケット名]'
    file_path = 'weather.png'
    response = s3.get_object(Bucket=bucket, Key=file_path)
    image_data = response['Body'].read()
    files = {'file': image_data}
    param = {'token': '[トークン]', 'channels': '[チャンネルID]', 'initial_comment': '<@[ユーザーID(メンション用)]>'}
    res = requests.post(url="https://slack.com/api/files.upload",params=param, files=files)
    return res.text

再度次のコマンドを実行してdeploy_package.zipを作成してLambdaにアップロードして下さい。

$ docker run --rm -v "${PWD}":/var/task lambda_headless_chrome

テストしてSlackに画像がアップロード出来ることを確認したらCloudWatchから先程作ったルールのターゲットに、今回のLambdaを追加します。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA