AWS上でWebスクレイピング環境を構築
本記事は、たくさんの先輩方の知見に基づいて作成できています。
やりたいこと
定期的に特定のサイトから情報取得する
取得した情報をAWS上に保存する
保存した情報を外部から参照できるようにする
手順(環境構築)
AWS Management Console にログイン
EC2 pyenv, selenium インストール
https://note.com/yuu________/n/nbe90710b842bEC2 chrome インストール
https://note.com/yuu________/n/n75d185d30e26EventBridge でEC2を起動、実行、終了を設定
・EC2の起動・終了は、ロールくらいなので割愛。EventBridge でEC2実行 の注意点
・「Systems Manager Run Commandでシェルスクリプト実行」以降の
Rulesを作成して実行する部分を参考にする。
https://www.d-make.co.jp/blog/2021/11/20/ec2-cron-to-eventbridge-rule/
python実行する際に注意点は、Pathが有効になっていないこと。コンソールログインの状態と、Rulesから実行される状態は異なる。
パス設定をスクリプト内に含める必要あります。Run-Commandの権限
権限でちょっとはまったのでメモ
https://qiita.com/haraitai00/items/1ff4da073dfdde214f0b
ソースコード
Rulesから実行するスクリプト
#!/bin/bash
export HOME=/home/xxxxxxx
if [ -f "$HOME/.bash_profile" ]; then
source "$HOME/.bash_profile"
fi
DATE=`date`
# コピー先のS3バケット
S3_BUCKET=s3://xxxxxxxx/yyyyyyyy/
#---------------------------
# Pythonスクリプトを実行
#---------------------------
echo "[$DATE]grubs data..." >auto-exe.log
#echo "[$DATE]$PATH" >>auto-exe.log
pyenv global 3.12.3
python gtg.py
pyenv global system
#--------------------------
# CSVファイルをS3へ
#--------------------------
CSV_FILES=$(ls *.csv)
# ファイルが存在する場合のみ処理を行う
if [ -n "$CSV_FILES" ]; then
for FILE in $CSV_FILES; do
# CSVファイルをS3にコピー
aws s3 cp "$FILE" "${S3_BUCKET}$FILE"
# コピーが成功したかどうかをチェック
if [ $? -eq 0 ]; then
# 成功した場合、CSVファイルを削除
rm "$FILE"
echo "[$DATE]$FILE をS3にコピーし、ローカルから削除しました。" >>auto-exe.log
else
# 失敗した場合、エラーメッセージを表示
echo "[$DATE]$FILE のS3へのコピーに失敗しました。" >>auto-exe.log
fi
done
else
echo "[$DATE]CSVファイルが見つかりませんでした。" >>auto-exe.log
fi
Lambda 関数(レイヤーにpandas、boto3を追加)
import json
import boto3
import pandas as pd
from io import StringIO
s3_client = boto3.client('s3')
def lambda_handler(event, context):
bucket_name = 'xxxxxxxx' # S3 バケット名を指定
prefix = 'yyyyyyyyy/' # CSV ファイルが置いてあるプレフィックスを指定
# S3 から CSV ファイルの一覧を取得
response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=prefix)
files = [item['Key'] for item in response.get('Contents', []) if item['Key'].endswith('.csv')]
data_frames = []
# 各 CSV ファイルを読み込み、データフレームに追加
for file_key in files:
csv_obj = s3_client.get_object(Bucket=bucket_name, Key=file_key)
body = csv_obj['Body'].read().decode('utf-8')
data = StringIO(body)
df = pd.read_csv(data)
data_frames.append(df)
# データフレームを結合
if data_frames:
combined_df = pd.concat(data_frames, ignore_index=True)
else:
combined_df = pd.DataFrame() # 空のデータフレーム
# JSON 応答の作成
json_result = combined_df.to_json(orient='records')
return {
'statusCode': 200,
'body': json_result,
'headers': {
'Content-Type': 'application/json'
}
}
使用したサービス
EC2:Python seleniumを実行し、csv形式のデータにする。
S3:csvファイル置き場
Lambda:API Gateway: 外部から保存した情報を参照(json応答)
EventBridge:Scheduler: EC2の起動・終了/ Rules: スクリプト実行
IAM:ロール管理
困ったこと
Lambdaのレイヤー作成
スクレイピングをLambda関数で行おうとしたが、EC2へIAM設定
AWSをはじめて触ったこともあり、権限の与え方が最初は困惑Rules経由で実行したときだけ動かなかった
権限不足など重なったこともありパスが効いていないことに気付くのが遅かった
雑感
はじめは、Lambda関数、S3だけで構築しようとしましたがLambda関数のレイヤー作成やバージョン互換が非常に面倒だと感じたので、EC2上に構築することにしました。Lambda関数が複雑だとレイヤーの保守やバージョン互換性、メモリサイズなど気にすることが多いです。疎結合で設計上は美しいかもしれませんが無駄に複雑になると思いました。
この定期的に実行する環境は、スクリプト内に処理を追加すれば拡張できて、データ置き場をS3からDynamoDBにすることも容易です。AWSって便利ですね。