こんにちは!札幌在住のフリーランス個人開発者、僕です。普段は車買取販売や守衛業務の傍ら、Pythonをメインに独学でゴリゴリとプログラミングに打ち込んでいます。今日は、僕がAzureの仮想マシン(VM)をフル活用して、大規模なWebデータ収集システムを構築した実録についてお話ししたいと思います。
1. 眠っていたAzure VMが目覚めるまで
事の発端は、僕がAzure上に構築していたWindows 11のクラウドPCでした。娘がMacBookを使っているのですが、どうしてもWindows環境が必要な時がある。そんな時でも手軽に使えるようにと、GUI操作が可能なWindows VMを契約していたんです。Remote Desktopで接続すれば、どこからでもWindowsデスクトップが使える便利さには満足していました。
でもある日、ふと「この仮想マシン、もっと有効活用できないか?」という思いが頭をよぎりました。月々そこそこのコストを払っているのに、たまに使うだけの“おもちゃ”ではもったいない。
そこで僕が思いついたのが、「大規模なWebデータ収集システムに転用する」というアイデアでした。具体的には、某大手口コミサイトから全国の店舗情報やレビューを収集し、自分だけの巨大なデータベースを構築することです。
しかし、Windows VMはGUI中心でリソース効率が悪いのが難点。大規模なバックエンド処理には不向きです。そこで僕は一大決心。このWindows VMを、より軽量で効率的なLinux(Ubuntu)に入れ替えることにしました。
2. WindowsからLinuxへ、そして収集基盤の構築
Azure PortalからVMを停止し、OSディスクをUbuntu 24.04 LTSのディスクに交換。まさにOSを丸ごと入れ替える大手術です。VMのサイズは「Standard_B2s」(2vCPU, 4GB RAM)を選定。このスペックで、月額コストは約3,000円程度に収まります。個人開発者のお財布にも優しいのが嬉しいところ。
新しいUbuntu VMには、まずSSH鍵認証を設定してセキュリティを確保。Azureのネットワークセキュリティグループ(NSG)でSSH(22番)ポートだけを開放し、外部からの安全な接続経路を確保しました。TailscaleのようなVPNは導入せず、シンプルなPublic IPでの直接接続を選んでいます。
次に、データ収集の心臓部となるPython環境を構築。Python 3.12をインストールし、venvで仮想環境を作成。必要なパッケージ(requests、beautifulsoup4、lxml)をサクッとインストールしました。
データベースには、ファイルベースで管理が容易な「SQLite3」を採用。別途データベースサーバーを立てる必要がなく、Pythonスクリプトから直接アクセスできる手軽さが個人開発には最適だと判断しました。
3. 分散型クリーンデータ収集システムの全貌
僕が構築したデータ収集システムは、自宅のMac miniとAzure VMの2台構成です。
- 自宅Mac mini(常時稼働): 全国統合されたメインDB(765万件超)を保持。全体の管理と、Azure VMで収集されたデータの取り込みを担当。
- Azure VM(バースト利用): 全国を10地方(北海道〜九州)に分割し、それぞれのエリアごとの「クリーンデータベース」構築を実行。
なぜ2台構成にしたかというと、自宅回線だけだと、ある特定のIPアドレスにWebサイト側からの負荷が集中してしまうリスクがあるからです。Azure VMを介することで、IPアドレスを分散させ、より安定した収集を目指しました。
クリーン収集の仕組みは以下の通りです。
全国を10の地方に分割し、各地方ごとに独立したSQLiteデータベースを生成します。そして、16ワーカーの並列スクレイピングと、収集したデータをデータベースに書き込むためのスレッドを分離(キューベース)することで、効率的なデータ投入を実現しました。site_review_idをUNIQUEキーとすることで、重複なしのクリーンなデータベースを構築しています。
このクリーン収集で、首都圏を除く9地方で合計約335万件、データ量にして約3.9GBもの口コミデータを収集することに成功しました。
各地方の収集結果は以下の通りです。
- 四国: 159,128件 / 57分
- 北関東: 148,099件 / 51分
- 北海道: 224,499件 / 81分
- 東北: 208,785件 / 58分
- 北陸: 243,536件 / 76分
- 東海: 755,577件 / 224分(約3.7時間)
- 近畿: 742,313件 / 212分(約3.5時間)
- 中国: 167,898件 / 52分
- 九州: 701,099件 / 200分(約3.3時間)
このように、Azure VMは週末などの空いた時間にバースト的に稼働させ、短時間で大量のデータを収集する役割を担っています。
4. AIと共創した差分収集システムの開発
クリーン収集が完了した後、次に直面した課題は「日々の新規データをどう効率的に追加していくか?」という点でした。毎日数時間かけて全件を再収集するのは現実的ではありません。そこで差分収集システムの開発に着手しました。
この設計段階で、僕は画期的な試みをしました。なんと、6つのAI(Claude, Gemini, Grok, Perplexity, OpenAI, Mistral)に同時に設計相談を持ちかけたのです!
「毎日更新される口コミサイトから新規データだけを効率的に取得したい。既存DBとの突合と、どこで収集を打ち切るかのロジックを教えてほしい」という具体的な問いかけに対し、全AIがほぼ共通して指摘したのが、「early stopは1件で止めるな、連続N件の既知IDで判定しろ」というアドバイスでした。これは目から鱗で、実際のサイトの更新状況を考えると非常に理にかなった指摘でした。AI時代の個人開発、すごいと改めて実感した瞬間です。
このAIからのフィードバックを元に、独立したスクリプト方式を採用し、共通モジュールを切り出してリファクタリングを実施しました。
結果として、差分収集の効率は劇的に改善。全9地方をわずか18分で完了できるようになりました。全件収集だと数時間かかっていた処理が、たった18分です。四国地方でのテストでは、0.6分で266件の新規データを正確に検出することに成功しています。
5. 高速・堅牢なシステムを支える技術のポイント
このシステムを支える主要な技術的ポイントをいくつか紹介します。
- SQLiteのWALモード: Write-Ahead Logging (WAL) モードを有効にすることで、読み込みと書き込みの並行処理が可能になり、データベースのI/O性能が向上しました。
- ThreadPoolExecutor + Queue: ワーカーはHTTPリクエストによるデータ取得に専念させ、取得したデータはPythonのQueueに投入。データベースへの書き込みは単一のスレッドに集約することで、DBロックを回避し、データの整合性を保ちながら高速化を実現しました。
- 効率的なearly stop: AIのアドバイス通り、連続5件の既知IDを検出したらその店舗の口コミ収集を打ち切るロジックを採用。これにより、不要なリクエストを減らし、効率と正確性のバランスを取りました。
- セッション管理: Webサイトからのブロックを避けるため、User-Agentのローテーションを行い、さらに100ページごとにセッションを再生成することで、新しい接続として振る舞い、安定した収集を維持しました。
- 閉店店舗の自動検知: HTTP 403 (Forbidden) や302 (Found) といったステータスコードを検出することで、すでに閉店している店舗やアクセスできないページを自動的にスキップする仕組みを導入し、無駄な処理を削減しました。
6. まとめ:個人開発者がAzureでできること
このシステムは、共通モジュール化によって保守性も向上させています。
- ch_collector_core.py: セッション管理、HTMLパーサー、ReviewWriter、DB操作といった基幹機能を凝縮。
- ch_clean_collect.py: 全件クリーン収集スクリプト(coreをimport)。
- ch_diff_clean.py: 差分収集スクリプト(coreをimport)。
これにより、もし収集対象サイトのHTML構造に変更があったとしても、ch_collector_core.pyの1箇所を修正するだけで対応できる堅牢な設計になっています。
僕のような札幌在住のフリーランス、プログラミング独学の個人開発者でも、Azure VMの月額約3,000円というコストで、これほど大規模で効率的なデータ収集システムを構築できる時代になったんです。眠っていた仮想マシンが、ちょっとした発想の転換と技術への好奇心、そしてAIの力を借りることで、ここまで進化を遂げるとは思ってもみませんでした。
「時間リッチ」な生活を目指す僕にとって、このようなシステムは新たな価値を生み出す源泉となっています。これからもAzureを始めとするクラウドサービスと、AIの力を最大限に活用しながら、様々な個人開発に挑戦していきたいと思います。この記事が、誰かの新しい挑戦のきっかけになれば幸いです!
著者: ふじのすけ
作成: Gemini 2.5 Flash + Claude Opus 4.6
投稿日: 2026-03-21 14:10 JST
コメント
コメントを投稿