DOMの墓場で消耗するな:GPT-4oで「自律型スクレイピングエージェント」を構築する

DOMの墓場で消耗するな:GPT-4oで「自律型スクレイピングエージェント」を構築する

「またCSSセレクタが死んでるぞ」――早朝のSlackに飛んでくるアラート。開発者の朝は、壊れたスクレイパーの修正から始まる。

2024年、モダンなWebサイトは動的かつ複雑だ。ReactやVueが生成するカオスなDOM構造を、血眼になって追いかける時代はもう終わった。セレクタのメンテナンスに人生を捧げるのは、もはやエンジニアリングではない。ただの「DOMの奴隷」だ。

私は最近、PlaywrightとGPT-4oを組み合わせた自律型エージェントを構築し、この不毛な戦いに終止符を打った。これは単なるAPIラッパーではない。「失敗を許容し、自己修復する」ための泥臭いエンジニアリングの記録だ。


1. なぜ「セレクタ」ではなく「意味」を抽出するのか

現代のWebスクレイピングにおいて、セレクタ指定は極めて脆弱だ。クラス名が`css-1a2b3c`のようにハッシュ化される環境下では、セレクタは数日で陳腐化する。

ここでAIエージェントの出番だ。AIはDOMのツリー構造ではなく、人間が認識する「UIの文脈」を理解する。これにより、メンテナンスコストを定量的に激減させた。具体的には、HTMLをそのまま投げるのではなく、`Readability.js`等を用いてMarkdown形式に変換し、トークン効率を最大化するというアプローチをとる。

トークン効率を最大化するDOM変換

`document.body.innerText`で生のテキストを投げつけるのは素人のやることだ。ノイズを削ぎ落とし、意味のある構造だけをAIに渡す。

from readability import Document
import html2text

def get_clean_markdown(page_content: str) -> str:
    # 不要なスクリプトタグなどを削除し、Markdownに変換
    doc = Document(page_content)
    text = html2text.html2text(doc.summary())
    return text[:8000] # トークン消費を抑えつつ、重要なコンテキストを維持


2. 「無限ループ」を封じる:状態管理の設計

AIエージェント構築最大の罠は「無限ループ」だ。目的を達成するまでボタンを押し続けるAIは、気付かぬうちにAWSの料金を溶かす。

これを防ぐには、「実行履歴のコンテキスト管理」が不可欠だ。直近のアクションをスタックし、同じ操作が繰り返された場合に強制的にロジックを分岐させる。

class AgentContext:
    def __init__(self):
        self.history = []

    def update(self, action):
        self.history.append(action)
        if len(self.history) > 3 and all(x == action for x in self.history[-3:]):
            raise Exception("無限ループ検知:同一アクションの連続")

# 実行ループ内での制御
try:
    action = get_next_action(page_markdown)
    context.update(action)
    await execute_action(page, action)
except Exception as e:
    logger.error(f"エージェントが迷子になりました: {e}")
    # フォールバック処理へ


3. コストと速度を最適化する:エンジニアリングの解法

「APIを叩くたびに数円かかる」という愚痴をこぼす前に、アーキテクチャを見直すべきだ。

戦略内容効果
モデルの使い分け構造解析にはGPT-4o mini、推論にはGPT-4oコストを約1/10に削減
キャッシュ戦略ページ構成が変わらない場合はキャッシュを返す速度向上とトークン節約
並列実行PlaywrightのBrowserContextを分離して並列化待機時間のボトルネック解消

特に「LLMの推論待ち」をいかに減らすかが重要だ。全てのページをAIに判断させるのではなく、まずは正規表現や軽量なパーサーでフィルタリングし、AIには「最終的な判断」だけを委ねるハイブリッド構成が、実運用では最も安定する。


4. 「完全自律」という幻想を乗り越える

正直に言おう。Cloudflareの検知や、複雑なOAuth認証をAIだけで突破するのは、現時点では「泥沼への招待状」だ。

私がたどり着いた結論は、「AIは判断まで、認証は人間(または固定Cookie)」という役割分担だ。Playwrightで`stealth`プラグインを使い、セッションを永続化する。AIには「ログイン後のページ遷移」や「データ抽出」という、最も人間がやりたくない作業を任せる。

結論:AIは免罪符ではない

AIを使ったからといって、エラーハンドリングをサボっていい理由にはならない。むしろ、AIという「不確定な要素」が加わる分、従来のスクレイピングよりも強固な防壁(ガードレール)をコードで書く必要がある。

  • 失敗したときにどう死ぬか(例外処理)
  • コストをどう監視するか(予算アラート)
  • AIの判断をどう検証するか(バリデーション)
これらを設計できないなら、CSSセレクタと心中している方が、まだマシな結果を生むだろう。AIは最強の道具だが、使う奴がポンコツなら、ただの「高額なゴミ生成機」に成り下がる。

さあ、次はどのサイトのDOMを破壊しに行く? 私は、AIにコードを書かせるのではなく、AIを「エンジニアの分身」として使いこなす側に回るつもりだ。