まさかの2年越しの続編投稿です
前回part3はこちら
前回part3はsuumoから不動産の販売情報をスクレイピングで取得してBing Mapにマッピングするというプログラムをpythonで書きました。まだ当時、AIコードアシスタントはメジャーではなかったので、チャッピーと壁打ちしながら、時にはソースそのままぶっ込んで修正おなしゃす!とかやりながら、仕上げていた記憶です。デプロイもしてなくて、ローカル上でしこしこ動かして悦に入っていたっぽい。
2年越しの投稿ということで、もう少し私の成長っぷりをみせつけたい。そうはいっても概ねAI駆動開発の進歩の産物なんですが(笑)本人のスキル向上とは別のところで、技術進歩により開発工程も、アウトプットもぐっと洗練された様子をお届けしようと思います。
今回やったこと
①データ取得をサーバサイドで実行し、最新の情報を非同期で反映できるようにする。②①で取得した販売情報データを地図にマッピングして、可視化する。フィルタ機能をつけて、分析しやすくする。それをどこかの環境にデプロイして、サービスとしてプレビュー版公開する。(自分でみてニヤニヤするだけ)
(注記)スクレイピング処理の実行について
スクレイピングはデータ活用における強力な武器である一方、やり方を間違えると「サーバー攻撃とみなされるリスク」や「法的トラブルにつながるおそれ」があります。本記事におけるスクレイピング処理の実行にあたっては、以下の点を前提として実施しています。
対象となるWebサイト(SUUMO)については、事前に利用規約を確認し、本記事のような「私的な学習の範囲」かつ「通常の利用を妨げない範囲」において、明確に禁止されていないことを確認した上で実施しています。また、使用するプログラムには不正アクセスやデータの破壊など有害な動作を行う意図・機能は一切含まれていません。
スクレイピングの是非は、対象サイトの規約や実施方法・手法等によって判断が分かれます。明確に禁止が謳われている場合以外でも、スクレイピングマナーを守り、相手方のサーバ負荷への最大限の配慮を行うとともに、営利活動への影響がないようにしましょう。
(スクレイピングマナー)
・Time.sleep(アクセス間隔)の確保: 連続的なリクエストによるサーバーダウンや帯域圧迫を防ぐため、リクエストごとに十分な待機時間(数秒〜数十秒)を設け、人間がブラウザで閲覧するのと同等、あるいはそれ以下の頻度に留める。
・オフピークタイムでの実行: サービスの利用者数が多い時間帯を避け、サーバー負荷が低い深夜や早朝などのオフピーク時間に限定してプログラムを実行する など
①データ取得(スクレイピング)の自動化
改めて作成してもよろしいのですが、過去の作ったものをリバースエンジニアリングする形で制作していこうと思います ※Geminiが
こんな雑プロンプトから、次のような提案をしてくれました。
1. Pythonコードの修正
ローカル実行用のコードを、Lambdaで動くように修正します。主な変更点は以下の2つです。
・処理全体を lambda_handler 関数内に収める。
・to_csv でのローカル保存を廃止し、boto3 を使ってメモリ上から直接S3へアップロードする。
修正後のコードイメージ (app.py)
import pandas as pd
import boto3
import io
import os
# S3設定
S3_BUCKET_NAME = "your-bucket-name" # 作成したバケット名
S3_FOLDER_NAME = "suumo_data/"
def lambda_handler(event, context):
print("job start")
# ... スクレイピング処理 (省略) ...
# DataFrame作成
df = pd.DataFrame(data, columns=['物件名', ...])
# CSVをメモリバッファに書き込み
csv_buffer = io.StringIO()
df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
# S3へアップロード
s3 = boto3.client('s3')
file_name = f"suumo_bukken_{date_string}.csv"
key = f"{S3_FOLDER_NAME}{file_name}"
s3.put_object(Bucket=S3_BUCKET_NAME, Key=key, Body=csv_buffer.getvalue())
print(f"Uploaded to s3://{S3_BUCKET_NAME}/{key}")
return {'statusCode': 200, 'body': 'Success'}
2. 環境構築(Dockerを使ったデプロイ)
pandas や BeautifulSoup4 (bs4) はLambdaの標準環境に含まれていません。Lambda Layersを使う手もありますが、依存関係を確実に管理するためにDockerコンテナイメージを使ってデプロイする方法を採用しました。
ディレクトリ構成
project-folder/
│
├─ app.py # 修正したPythonコード
├─ requirements.txt # 依存ライブラリ
└─ Dockerfile # コンテナ定義
requirements.txt (依存ライブラリ)
ここで重要なのが numpy のバージョンです。最新版だとLambda環境でのビルド時にコンパイルエラー(gcc不足)が発生したため、バイナリが提供されている安定版を指定しました。
Dockerfile (コンテナ定義)
AWSが提供するLambda用のベースイメージを使います。
FROM public.ecr.aws/lambda/python:3.11
# 依存関係をインストール
COPY requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install -r requirements.txt
# スクリプトをコピー
COPY app.py ${LAMBDA_TASK_ROOT}
# 実行ハンドラを指定
CMD [ "app.lambda_handler" ]
3.AWS CloudShellを使ったデプロイ
ローカルPCにDocker Desktopを入れるのは重いし手間がかかるため、AWS上のブラウザで使えるターミナル AWS CloudShell を使ってビルドを行いました。これが一番手軽です。
手順
(1)ECRリポジトリの作成
AWSコンソールで Elastic Container Registry (ECR) を開き、リポジトリを作成します(例: suumo-scraper)。
(2)CloudShellでビルド & プッシュ
CloudShellに3つのファイルをアップロードし、ECRの「プッシュコマンド」に従ってコマンドを実行します。
# ECRへのログイン
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com
# ビルド
docker build -t suumo-scraper .
# タグ付け & プッシュ
docker tag suumo-scraper:latest [URI]:latest
docker push [URI]:latest
4. Lambda関数の作成と設定
AWS Lambdaコンソールで関数を作成します。
(1)関数の作成
「コンテナイメージ」を選択し、先ほどプッシュしたECRイメージを指定。
(2)権限設定
実行ロールに AmazonS3FullAccess(または対象バケットへの書き込み権限)を付与。
【重要】タイムアウトとメモリの設定
スクレイピングは処理時間が長く、メモリも消費します。デフォルト設定(3秒 / 128MB)では確実に失敗するため、以下のチューニングを行いました。
メモリ: 512MB → 2048MB (2GB)
※512MBでは処理途中でメモリ不足エラーが発生しました。
タイムアウト: 3秒 → 10分
※Lambdaの最大実行時間は15分。
5. 実行結果
テスト実行を行い、CloudWatch Logsで job end を確認。 指定したS3バケットを見に行くと、無事にCSVファイルが生成されていました!
S3バケット
csvの中身
GitHubリポジトリ
今回のコード一式はこちらで公開しています。
https://github.com/misonosuke/suumo-scraper
なおこの先の作業をやっていて、地図にマップするための緯度経度情報がないことに気づき、後でgeocoderで住所から緯度経度情報与えるプログラムを追加作成しています。
ここまででだいぶ盛りだくさんな内容となったため、デプロイ②については後半part5に
持ち越しさせていただきたいと思います。
乞うご期待。
後半part5はこちら


