# agentにWebページのデータを取らせるために、3つの不器用な方法を試して、最後に1つだけ残した

agentにWebページのデータを取らせるために、私は{前後|ぜんご}して3つの{不器用|ぶきよう}な{方法|ほうほう}を{試|ため}した。

いちばん{直接的|ちょくせつてき}なのは、ページ{全体|ぜんたい}のHTMLをモデルに{渡|わた}して{自分|じぶん}で{読|よ}ませることだ。{中|ちゅう}くらいのページでも{数万|すうまん}tokenになり、データをまだ{取|と}れていないのに、コンテキストを{先|さき}に{半分|はんぶん}ほど{使|つか}い{切|き}ってしまう。モデルもよく{行|ぎょう}を{取|と}り{違|ちが}える。2つ{目|め}はCSSセレクターを{自分|じぶん}で{書|か}き、1フィールドごとに`find`を1{回|かい}、さらに`get`を1{回|かい}するやり{方|かた}だ。10{個|こ}を{超|こ}えるフィールドなら10{回|かい}を{超|こ}える{往復|おうふく}になり、{遅|おそ}くて{冗長|じょうちょう}だ。3つ{目|め}はJSの`eval`を{手書|てが}きしてページに{流|なが}し{込|こ}んで{実行|じっこう}すること。{使|つか}えはするが、ページごとに{書|か}き{直|なお}す{必要|ひつよう}があり、{属性|ぞくせい}の{取得|しゅとく}も、{複数値|ふくすうち}の{取得|しゅとく}も、すべて{自分|じぶん}で{面倒|めんどう}を{見|み}ることになる。

{一周|いっしゅう}まわって{残|のこ}ったのは、chrome-useの2つのコマンドだ。{既知|きち}のサイトには`site`、{任意|にんい}のページには`extract`を{使|つか}う。{背後|はいご}にある{判断|はんだん}は{同|おな}じで、データ{取得|しゅとく}は1{回|かい}の**{宣言的|せんげんてき}**な{評価|ひょうか}であるべきで、**{命令的|めいれいてき}**な{往復|おうふく}の{列|れつ}ではない、ということだ。

![1つのコマンドで実際にログイン済みのChromeからきれいなJSONを取得し、ページ全体のHTML、壊れたselector、アンチボット壁という3つの古い道が消されている](https://img.leeguoo.com/media/dc4b3b83-f883-4adc-88e1-c9ae6de89666/extract-hero.webp)

## site:{既知|きち}のサイトなら、セレクターを{自分|じぶん}で{書|か}かない

chrome-useが{認識|にんしき}しているサイトを{開|ひら}くと、{利用可能|りようかのう}なadapterがあると{自動|じどう}で{示|しめ}してくれる。

```
$ chrome-use open https://news.ycombinator.com
💡 site adapters for news.ycombinator.com — prefer these for structured data:
   hackernews/thread, hackernews/top
```

adapterは、{特定|とくてい}のサイトに{向|む}けてあらかじめ{書|か}かれたデータ{取得|しゅとく}ロジックだ。HNのDOMがどんな{形|かたち}か、これまで{何度|なんど}{変|か}わったかを{気|き}にする{必要|ひつよう}はない。

```bash
chrome-use site hackernews/top --json
```

{返|かえ}ってくる1{件|けん}ごとのデータは{完全|かんぜん}で、タイトルだけではない。

```json
{ "rank": 1, "title": "ZCode – Harness for GLM-5.2", "url": "https://zcode.z.ai/en",
  "author": "chvid", "score": 329, "comments": 258, "id": 48753715,
  "hn_url": "https://news.ycombinator.com/item?id=48753715" }
```

adapterの{本当|ほんとう}の{価値|かち}は、「ページ{構造|こうぞう}は{変|か}わる」という{問題|もんだい}をあなたの{側|がわ}から{切|き}り{離|はな}してくれることだ。HNが{改版|かいはん}されればadapterがそれに{合|あ}わせて{直|なお}される。あなたの`site hackernews/top`は1{文字|もじ}も{変|か}えなくていい。{代償|だいしょう}は、{対応|たいおう}するのが{内蔵|ないぞう}されたサイトだけに{限|かぎ}られることだ。まず`chrome-use site`で{何|なに}があるかを{見|み}ればいい。

{実際|じっさい}につまずきやすい{小|ちい}さな{点|てん}が2つある。データは{戻|もど}り{値|ち}のトップレベルではなく`.data.result.posts`にあるので、`jq`と{組|く}み{合|あ}わせるなら{完全|かんぜん}なパスを{書|か}く{必要|ひつよう}がある。もう1つ、chrome-useはstderrにバージョン{通知|つうち}を1{行|ぎょう}{出|だ}す。それはstdoutのJSONには{影響|えいきょう}しないが、{完全|かんぜん}にきれいにしたいなら`2>/dev/null`を{付|つ}ければいい。

## extract:{任意|にんい}のページには、セレクターschemaを{渡|わた}す

{既製|きせい}のadapterがないサイトでは`extract`を{使|つか}う。ここでまず{誤解|ごかい}を{正|ただ}しておきたい。私も{最初|さいしょ}は{間違|まちが}えたが、これは「{欲|ほ}しいものを{自然言語|しぜんげんご}で{説明|せつめい}する」ものではない。{渡|わた}すのは**CSSセレクター**のschemaだ。

```
{
  "rows": "<css>",           // rowsがある:反復コンテナ → 配列を返す。ない → ページ全体に対して1つのオブジェクトを返す
  "fields": {
    "name": ".title",                          // 短縮形:セレクター → trim後のテキスト
    "href": { "sel": "a", "get": "@href" },    // 属性を取得
    "tags": { "sel": ".tag", "get": "text", "all": true }  // 一致したものを全部取得 → 配列
  }
}
```

`get`には`text`（デフォルト）/ `@属性` / `html` / `value`を{指定|してい}できる。HNのトップページから{各|かく}{項目|こうもく}のタイトルとリンクを{取|と}るなら、{行|ぎょう}コンテナは`.athing`だ。

```bash
chrome-use extract --schema '{"rows":".athing","fields":{"title":".titleline a","url":{"sel":".titleline a","get":"@href"}}}'
```

```json
[ { "title": "ZCode – Harness for GLM-5.2", "url": "https://zcode.z.ai/en" },
  { "title": "Oomwoo, an open-source robot vacuum you build yourself", "url": "https://makerspet.com/blog/..." } ]
```

`rows`を{書|か}かなければ、ページ{全体|ぜんたい}に{対|たい}して1つのオブジェクトを{返|かえ}す。`h1`や`link[rel=canonical]`のようなページ{単位|たんい}のフィールドを{取|と}るのに{向|む}いている。

もう{一段|いちだん}{深|ふか}く{考|かんが}える{価値|かち}があるのは、その{実行方法|じっこうほうほう}だ。schema{全体|ぜんたい}は**1{回|かい}のページ{評価|ひょうか}にコンパイル**される。{前|まえ}のように1フィールドごとに{往復|おうふく}するわけではない。10{個|こ}を{超|こ}えるフィールドでも1{回|かい}で{取|と}り{戻|もど}せる。{速|はや}く、{挙動|きょどう}も{確定的|かくていてき}だ。{同|おな}じschemaを{同|おな}じページに{当|あ}てれば、{結果|けっか}は{安定|あんてい}して{再現|さいげん}できる。ここが、データ{取得|しゅとく}において{宣言的|せんげんてき}なやり{方|かた}が{命令的|めいれいてき}なやり{方|かた}より{扱|あつか}いやすい{理由|りゆう}だ。{構造|こうぞう}を{記述|きじゅつ}すれば、あとは1{回|かい}で{計算|けいさん}してくれる。セレクターの{書|か}き{方|かた}がわからないなら、まず`snapshot -i`で{構造|こうぞう}を{見|み}るか、DevToolsで{対象|たいしょう}{要素|ようそ}をクリックしてselectorを{取|と}ればいい。

## {取|と}れないもの、そして{使|つか}うべきでないもの

{単一|たんいつ}コンテナモデルにも{届|とど}かない{場合|ばあい}がある。HNのスコアは`.athing`の{行|ぎょう}にはなく、すぐ{次|つぎ}の{行|ぎょう}の`.subline`にある。そのため、{上|うえ}の{例|れい}ではタイトルとリンクだけが{取|と}れている。「1{件|けん}のデータが2{行|ぎょう}のDOMに{分|わ}かれている」{構造|こうぞう}では、`rows`/`fields`の{仕組|しく}みだけでは{全部|ぜんぶ}を{取|と}れない。まさにこれがadapterの{存在理由|そんざいりゆう}だ。`site hackernews/top`はすでに2{行|ぎょう}を{結合|けつごう}してくれている。

{逆|ぎゃく}に、そもそもブラウザを{開|ひら}くべきでない{場面|ばめん}もある。{公開|こうかい}された{静的|せいてき}ページの{数個|すうこ}のフィールドなら、`curl` + `jq`のほうが{速|はや}い。「どんなページがあるか」を{知|し}りたいなら{検索|けんさく}エンジンを{使|つか}うべきだ。`extract` / `site`の{本当|ほんとう}の{主戦場|しゅせんじょう}は、ログインが{必要|ひつよう}で、JSを{実行|じっこう}する{必要|ひつよう}があり、アンチボットに{止|と}められるページだ。chrome-useが{操作|そうさ}するのは、あなたがその{時点|じてん}でログインしている{本物|ほんもの}のChromeだからだ。それらのデータはもともと{目|め}の{前|まえ}にレンダリングされている。chrome-useは、{画面|がめん}にあるものをJSONとして{読|よ}み{取|と}っているだけだ。[CreepJS](https://abrahamjuliot.github.io/creepjs/)で{実測|じっそく}したbotスコアは0%。{偽装|ぎそう}しているからではなく、それがもともとあなたのブラウザだからだ。

データ{取得|しゅとく}で{難|むずか}しいのは、{解析|かいせき}そのものではない。{解析|かいせき}できる{段階|だんかい}までまず{到達|とうたつ}することだ。{前半|ぜんはん}のログイン、レンダリング、{検出|けんしゅつ}の{通過|つうか}はあなた{自身|じしん}のブラウザに{任|まか}せる。{後半|こうはん}では、`site`が{組|く}み{立|た}て{済|ず}みのレコードを{渡|わた}し、`extract`が1つのセレクターで{一度|いちど}にきれいに{取|と}らせてくれる。

関連ドキュメント（英語）：[chrome-use · Collect Data](https://chrome-use.leeguoo.com/en/extract.html)、[インストール](https://chrome-use.leeguoo.com/en/install.html)。
