【ハッカソン開発記#6】Pythonのスクレイピング処理をFastAPIでWeb API化・拡張可能な設計にする
概要
ハッカソンで作った「スクレイピング&Gemini APIでライブ情報を取得するPythonスクリプト」を、Next.js(フロントエンド)から呼び出せる本格的なWeb API(FastAPI)へとリファクタリングしました(ここは期間の都合で、結構AI使いました。6割ぐらい)。
1. アーキテクチャの改善(拡張性への対応)
元々は1つの会場(マリンメッセ)専用のベタ書きスクリプトでしたが、将来的な会場追加(Zepp、ドームなど)を見据えてオブジェクト指向の設計に変更しました。
やったこと
-
抽象クラス(設計図)の導入 (
base.py)-
VenueScraperBaseクラスを作成し、@abstractmethodで「セレクター取得」や「テキスト取得」のメソッドを定義
-
-
会場レジストリ(辞書)による管理 (
__init__.py)- フロントからの「会場名」と「実行するクラス」の対応表(
VENUE_REGISTRY)を作成
- フロントからの「会場名」と「実行するクラス」の対応表(
2. FastAPIの導入
コンソール出力で終わっていた処理を、常時稼働してJSONを返すAPIに変更しました。
ポイント
-
Pydanticによる型定義とバリデーション
- JAVA、TypeScriptで学んだ静的言語へ
-
CORS設定
- フロントエンド(
localhost:3000)からバックエンド(localhost:8000)へアクセスできるようにCORSMiddlewareを設定
- フロントエンド(
-
状態を持たないクラスの廃止
- 環境変数を取るためだけの
EnvGetterクラスは廃止し、シンプルにos.getenv()を使うように修正(クラスにしていたけど、状態を持たないので、わざわざ作る必要はなさそうという判断)
- 環境変数を取るためだけの
# Main.py のエンドポイント部分のイメージ
@app.post('/api/search', response_model=SearchResponse)
def search(req: SearchRequest):
# 1. 会場名から適切なスクレイパーを自動取得
scraper = get_scraper(req.venue)
# 2. スクレイピング & キーワードフィルタ
raw_texts = scraper.get_formatted_data()
filtered = DataFilter().getWordInKeyword(raw_texts, req.keywords)
# 3. Gemini APIでJSON化して返す
json_str = JsonGetterByAPI().getJson(filtered, api_key)
events = [LiveEvent(**e) for e in json.loads(json_str)]
return SearchResponse(venue=req.venue, events=events, total=len(events))
3. 動作確認とテスト方法
FastAPIの「Swagger UI」で簡単にテスト
Swagger UI(自動生成ドキュメント)
-
uvicorn Main:app --reloadでサーバー起動 - ブラウザで
http://localhost:8000/docsにアクセス - 自動生成されたUIからPOSTリクエストを送り、レスポンスのJSONを目視確認