はじめに
Lambda(Python)でスクレイピングしたくて Selenium とか ChromeDriver でゴニョゴニョしようとしたのですが、案の定エラーに詰まって動きませんでした。
Lambda 上で headless-chrome を使うために導入した serverless-chrome
と Python とのバージョン齟齬、Selenium と ChromeDriver のバージョン齟齬など、今回は Python、Selenium、ChromeDriver、serverless-chrome の各バージョンを揃える必要がありました。
serverless-chrome の Issue に上記の解決法が載っているで、今回はこの Issue を参考に AWS SAM や Lambda Layer を使って簡単なスクレイピングをしてみました。
Serverless Chrome?
Serverless Chrome には、AWS Lambda でヘッドレス Chrome の実行を開始するために必要なものがすべて含まれています(Azure 機能と GCP 機能は間もなく)。
このプロジェクトの目的は、サーバーレス関数の呼び出し中にヘッドレス Chrome を使用するための足場を提供することです。サーバーレス Chrome は、Chrome バイナリの構築とバンドルを処理し、サーバーレス機能の実行時に Chrome が実行されていることを確認します。
さらに、このプロジェクトは、一般的なパターンのいくつかのサンプルサービスも提供します(例:ページのスクリーンショットを撮る、PDF に印刷する、一部を削るなど)。
バージョン調整(2020/04/14 時点)
Issue を参考に動作確認を行ったバージョン関係です。
注意することは Python 3.8 では動作しないことでした。
- Selenium = 3.14.0
- Chrome Driver = 2.43
- Serverless Chrome = 1.0.0-55
- Python = 3.6
サンプル
とりまディレクトリ構造
あくまでもサンプルです。
sam init
してゴニョゴニョした後に完成するディレクトリ構造です。
不要なものは消しています。
1$ tree .
2.
3├── layer
4│ └── bin
5│ ├── chromedriver
6│ └── headless-chromium
7├── sample
8│ ├── __init__.py
9│ ├── app.py
10│ └── requirements.txt
11└── template.yaml
chromedriver 等の実行ファイルをダウンロード
1# LambdaLayer用のディレクトリ作成
2$ mkdir -p layer/bin
3
4# ダウンロード Chrome Driver 2.43
5$ wget https://chromedriver.storage.googleapis.com/2.43/chromedriver_linux64.zip -P layer/bin
6$ unzip layer/bin/chromedriver_linux64.zip
7
8# ダウンロード Serverless Chrome v1.0.0-55
9$ wget https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-55/stable-headless-chromium-amazonlinux-2017-03.zip -P layer/bin
10$ unzip layer/bin/stable-headless-chromium-amazonlinux-2017-03.zip
サンプル template.yaml
注意したいのは Python のランタイムが 3.6
であることです。
layer は、コンテナ上で /opt/
直下に配置されます。
layer/bin/***
であれば /opt/bin/***
に置かれます。
1AWSTemplateFormatVersion: "2010-09-09"
2Transform: AWS::Serverless-2016-10-31
3
4Globals:
5 Function:
6 Timeout: 30
7
8Resources:
9 ServerlessFunction:
10 Type: AWS::Serverless::Function
11 Properties:
12 FunctionName: sample-selenium-function
13 CodeUri: sample/
14 Handler: app.lambda_handler
15 Runtime: python3.6
16 Layers:
17 - !Ref ServerlessLayer
18
19 ServerlessLayer:
20 Type: AWS::Serverless::LayerVersion
21 Properties:
22 LayerName: sample-selenium-layer
23 ContentUri: layer/
requirements.txt に Selenium ライブラリを追加
Lambda Layer をせっかく使っているので layer/python/
等に固めるが良いのかなとも思っていますが、今回は requirements.txt で単純にライブラリ管理しています。
1cat << EOF >> sample/requirements.txt
2selenium
3EOF
サンプル app.py(Lambda)
Yahoo ニュースを対象に class から要素を print する簡単なサンプルになります。 詳しい操作方法はこちらの API ドキュメントを参考にしてもらえればです。
1import json
2from selenium import webdriver
3
4CHROME_DRIVER_PATH = "/opt/bin/chromedriver"
5HEADLESS_CHROMIUM_PATH = "/opt/bin/headless-chromium"
6
7TARGET_URL = "https://news.yahoo.co.jp/"
8
9def lambda_handler(event, context):
10
11 options = webdriver.ChromeOptions()
12 options.binary_location = HEADLESS_CHROMIUM_PATH
13 options.add_argument('--headless')
14 options.add_argument('--no-sandbox')
15 options.add_argument('--single-process')
16
17 chrome_driver = webdriver.Chrome(
18 executable_path = CHROME_DRIVER_PATH,
19 chrome_options = options)
20
21 chrome_driver.get(TARGET_URL)
22
23 print(chrome_driver.find_element_by_class_name('topics_title').text)
24
25 chrome_driver.close()
26 chrome_driver.quit()
27
28 return {
29 "statusCode": 200,
30 "body": json.dumps({
31 "message": "success",
32 }),
33 }
出力結果
1$ sam build
2$ sam local invoke
3>> トピックス
おわりに
全体的にバージョン低めなので慎重に運用する必要がありそうだなと思っていますが、比較的簡単にスクレイピングできてしまう Lambda はやっぱりすごいですね。
ひよっこですが、簡単なサービスをどんどん Lambda で組んでみたいです。