個人開発でスクレイピングをしています。
今回は Cloud Functions と ChatGPT を用いたスクレイピングのアーキテクチャを紹介します。
過去に書いたスクレイピングに関する記事はこちらです。
経緯
- もともと各 Web サイトに対して1つ1つスクレイピングのプログラムを実装していた。
- Web サイトのちょっとした変更に弱く、すぐに動かなくなってしまっていた。
- AI を導入することで変更に強い仕組みにした。
- DBへの挿入などの共通の仕様を変更するときの影響範囲がわかりにくいという問題もあった。
- AI を導入することを機にアーキテクチャを見直した。
過去のアーキテクチャ
次のように Web サイトの数だけ Scheduler や Functions を用意し、デプロイしていました。
このアーキテクチャでは1つのFunctions が1つのWebサイトの抽出・変換・読み込み・挿入などの処理を担当しています。
このアーキテクチャの問題点
この実装の場合は各 Functions のコードで重複が大量に発生していました。
例えば データベースへのデータの挿入や、データのバリデーションは全 Web サイトで共通の仕様です。
そういった共通の仕様をシェルスクリプトでコピーしてデプロイしており、共通仕様にちょっとした変更が入るだけでもすべての Functions をデプロイし直す必要がありました。terraform を使っているとは言え、あまり健全な感じがしませんでした。
また Git の履歴が scraping のすべてのファイルに残って汚くなりますし、ちょっとした変更が及ぼす影響範囲がつかみにくいところもあり、アンチパターンっぽさが漂っていました。
現在のアーキテクチャ
ChatGPT を導入することにしたときについでにアーキテクチャを見直すことにしました。
アーキテクチャは次のとおりです。
それぞれの役割を持った Functions が GCS にデータを置き、 GCS トリガーで次の処理が動くようになっています。
改善された点
- 共通の処理を抽出の Functions から切り離したことにより、変更を入れるときの変更箇所が明瞭になりました。
- フォーマット変換を AI に任せられるようになったため、実装の時間を大幅に短縮できるようになりました。
各Functions の役割
コンサート情報抽出
変換元となるコンサート情報を抽出します。1つの Functions 内に各 Webサイトの抽出スクリプトを用意し、Scheduler で渡されたWebサイト名に基づいて読み込むスクリプトを変更します。
抽出するのはWebサイトの URL, コンサート会場の ID、コンサート情報の生テキストです。
コンサート会場の ID はこのシステムが導入しているものであり、AIではどうしようもないため抽出時点で情報を付加します。
URL もページのテキストに含まれないため、この時点で付与します。
生のテキストはあとで AI で処理するため、雑に丸ごと取得できることが多く、この部分の実装は1つの Webサイトで早ければ数十分で終わります。
フォーマット変換
ChatGPT に対して生のテキストを渡し、テキストのフォーマットを変換します。
もともと手作業で Web サイトごとに日付やタイトルなどの情報の抽出を実装しており、1つのWebサイトで最低でも3時間程度、長いと1日かかっていましたが、テキストさえ渡せば良くなったのでとても楽になりました。
AI で取得した情報が間違っている場合に備えてバリデーションも同時にしています。
情報付加
コンサートのテキストを元に情報を付加しています。
例えばテキスト中にオーケストラという文字があれば、ジャンルにオーケストラを加えるといった処理です。
DB挿入
DBにデータを挿入します。
感想
もともと DRY 原則に反する感じのコードになっていましたが、改善できてよかったです。
terraform を使っているので別にデプロイの手間はかからないし、まあいっか!と思っていたのですが、コードを変更するのが徐々に難しくなっていくのを体感してアンチパターンを踏まないことは重要なんだなと思いました。
自然にアーキテクチャを考えたら ETL のパイプラインになっていて、データエンジニアとして自分自身も成長していることを実感しました。
今後も自分の作ったコードの面倒を見て技術力を上げるためにも個人開発を続けていきたいです。