
AI エージェント向けに Web ページの取得と抽出を 1 コマンドにまとめる CLI『ax』を触ってみた
ウィスキー、シガー、パイプをこよなく愛する大栗です。
Web フレームワーク Hono の作者である Yusuke Wada(yusukebe)氏が、「The AI-era curl(AI 時代の curl)」を掲げる CLI ツールの ax を公開しました。コーディングエージェントの Web ページ取得を効率化するツールとのことなので、実際に触ってみました。(個人的には、ゆーすけべーさんはいまだに Perl Monger の印象があったりします)
ax とは
ax は「fetch, discover, extract. One command.」を謳う、AI エージェント向けの HTTP / HTML 入出力ツールです。MIT ライセンスの OSS で、2026 年 7 月 16 日時点の最新版は v0.1.15 です。
コーディングエージェントに Web ページからのデータ抽出を依頼すると、curl で HTML を取得して、正規表現で解析する書き捨ての Python スクリプトを書く、というパターンをよく見かけます。公式サイトでは以下のような例が Before として挙げられています。
# エージェントが書きがちな書き捨てスクリプトの例
python3 - <<'PY'
import re
h=open('page.html',errors='replace').read()
blocks=re.findall(
r'<li class="lesson">([\s\S]*?)</li>',h)
rows=[]
for b in blocks:
href=re.search(r'href="([^"]+)"',b)
title=re.search(r'<a [^>]*>([^<]+)</a>',b)
...
PY
この方法では HTML の構造が少し変わるだけで破壊されますし、生 HTML をコンテキストに取り込むためトークンも浪費します。ax を使うと、同様の処理が以下のコマンドになります。
ax https://site.example '.lesson' \
--row 'title=a, href=a@href, level=.cefr'
title href level
Small talk /lesson/1.htm A2
Directions /lesson/2.htm A2
...
ax: note: 50 rows extracted, no empty fields
3 つの機能
ax の機能は fetch・discover・extract の 3 つに整理されています。
| 機能 | 説明 | 主なオプション |
|---|---|---|
| fetch | ステータス・ヘッダ・応答時間を含む構造化レポート付きで取得 | -X -H -d -u など curl 互換の書式 |
| discover | 生の HTML を出力せずにページの構造を把握 | --outline --locate 'text' --count |
| extract | CSS セレクタで構造化データを抽出 | --row --table --md --where |
エージェントでの利用を前提としているため、出力の設計がトークンを節約する内容となっているのが特徴です。
--budget <tokens>で出力全体をトークン数の予算で制御できる- 行データの出力はデフォルトで TSV(ヘッダは 1 回のみ出力、JSON 比で約 40% のトークン量)
- 出力の切り詰めは stderr に注記が表示され、暗黙に省略されることがない
- パースモードの URL は約 2 分間キャッシュされ、セレクタの試行錯誤で再取得が発生しない
従来手段との比較
書き捨て Python スクリプトや htmlq といった従来のローカル手段との比較がドキュメントに掲載されています。
| 機能 | 書き捨ての Python | curl + htmlq | ax |
|---|---|---|---|
| 完全なレポート(ステータス/ヘッダー/ミリ秒)を取得 | △ 処理を書く | ✗ | ✓ |
| 構造検出(--outline/--locate) | ✗ | ✗ | ✓ |
| CSSセレクタの抽出 | △ bs4が必要 | ✓ | ✓ |
| 1回の呼び出しで複数フィールドの行 ( --row) | △ 処理を書く | ✗ | ✓ |
| <table>→ キー付き行 | △ 処理を書く | ✗ | ✓ |
| マークアップ構造が変化した後のセレクター修理が容易 | ✗ (正規表現) | ✓ | ✓ |
| ページ → 読みやすいマークダウン ( --md) | ✗ | ✗ | ✓ |
| トークン形式の出力(制限、--budget、注釈) | ✗ | ✗ | ✓ |
| タスクごとにコード作成が不要 | ✗ | ✓ | ✓ |
ベンチマーク
Claude Code の実セッションを使ったベンチマーク結果も公開されており、curl + 書き捨てスクリプトと比較してコストが 23〜67% 削減されたとしています。
| タスク | コスト削減 |
|---|---|
| マークアップ変更のある 2 ページの抽出 | −67%($0.458 → $0.150) |
| 60 項目のカタログ抽出 | −65%($0.296 → $0.104、24 秒 → 14 秒) |
| おとり要素のある実サイトの処理 | −23%($0.248 → $0.191) |
| 初めての ax 利用(ドキュメント読み込み込み) | −58%($0.664 → $0.282) |
測定方法の詳細は bench/RESULTS.md に掲載されています。
実装
実装は TypeScript で、Bun により単一バイナリとして配布されています。ランタイム依存は DOM パーサの linkedom のみです。ローカルで決定論的に動作し、エージェントに組み込むツールとして安心できる設計だと思います。
エージェント向けの仕組み
ax はエージェント自身に使い方を教えるための仕組みを備えています。
| 仕組み | 内容 |
|---|---|
ax agent-context |
エージェント向けの完全なマニュアルをオフラインで標準出力に表示する |
npx skills add yusukebe/ax |
エージェントスキル(SKILL.md)としてプロジェクトに導入する |
| ax.yusuke.run/llms.txt | 全オプションのリファレンスを LLM 向けテキストで提供する |
単発のセッションなら ax agent-context の出力をプロンプトに貼り、継続的に使うならスキルとして導入する、という使い分けが想定されています。
インストール
インストール方法は複数用意されています。
# インストールスクリプト
$ curl -fsSL https://ax.yusuke.run/install | sh
# Homebrew
$ brew install yusukebe/tap/ax
# Nix
$ nix run github:yusukebe/ax
今回はローカルマシンの macOS に導入するため Homebrew でインストールしました。
$ brew install yusukebe/tap/ax
$ ax --version
0.1.16
やってみた
macOS(Apple Silicon)上の ax v0.1.16 で試していきます。せっかくなので、取得対象は DevelopersIO(dev.classmethod.jp)の私の過去記事と、弊社コーポレートサイト(classmethod.jp)にします。
Fetch: 構造化レポート付きの取得
まずはセレクタなしの Fetch モードです。以前書いた Tsumiki を Gemini CLI で動かした記事を取得してみると、ステータス・最終 URL・応答時間・主要ヘッダ・ボディが 1 つの JSON レポートにまとまって返ります。
$ ax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/
{
"status": 200,
"ok": true,
"url": "https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/",
"redirected": false,
"ms": 453,
"headers": {
"cache-control": "public, max-age=120",
"content-type": "text/html; charset=utf-8"
},
"headers_omitted": "16 (--headers for all)",
"body": "<!DOCTYPE html><html lang=\"ja\"><head><meta charSet=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>...",
"body_truncated": "394412 of 396412 chars hidden (--all or --budget T)"
}
ノイズになりがちなレスポンスヘッダは省略されますが、headers_omitted として省略した件数が告知されます。ボディの切り詰め(上記結果の body は長いため取得後に短縮しています)も body_truncated で明示されており、暗黙に何かを隠さない設計となっています。curl と違って空ボディやエラーステータスでも必ずレポートが返るため、エージェントが結果を誤解しにくくなっています。
Discover: ページ構造の把握
未知のページの構造を知りたいとき、生の HTML をダンプする代わりに --outline を使います。
$ ax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ --outlineax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ --outline
113 span
80 span.line
64 script
59 a.mb-2.block.text-footer-text.text-xs.hover:opacity-50
44 p.code-line
40 code
37 path
34 li.code-line
29 div
19 div.pb-1
19 a.text-footer-text.text-xs.hover:text-footer-text/50
18 div.animate-pulse.rounded-md.bg-skelton-background.h-2.w-full
12 button
11 div.code-block-container
11 svg.lucide.lucide-copy-icon.lucide-copy
...
ax: note: 18 more result(s) hidden (use --all, --limit N, or --budget T)
繰り返し出現するタグとクラスの組み合わせが件数付きで一覧されます。(長いので一部を省略しています)。本文の段落は p.code-line、コードブロックは div.code-block-container という構造が把握できます。件数の少ない要素は既定では隠れますが、--all を付けると本文見出しの h2.code-line(3 件)なども確認できます。
特定のテキストがどこにあるかは --locate で調べられます。記事中の「agent-slash-sync」の場所を探してみます。
$ ax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ --locate "agent-slash-sync"
[
{
"selector": "main.mx-auto.grid.gap-6.w-full.grid-cols-1.lg:max-w-screen-2xl.lg:grid-cols-[minmax(0,1fr)_calc(var(--spacing)*64)].lg:px-2.xl:grid-cols-[calc(var(--spacing)*64)_minmax(0,1fr)_calc(var(--spacing)*64)].2xl:grid-cols-[calc(var(--spacing)*64)_minmax(0,calc(var(--spacing)*213))_calc(var(--spacing)*64)] > div.w-full > div.bg-article-background.p-4.shadow-2xs.md:p-6 > article > header > div.my-4.text-article-text-muted",
"match": "AI 支援型テスト駆動開発フレームワークである『Tsumiki』は Claude Code が前提になっているのですが Gemini CLI に対応させて動かし…"
},
{
"selector": "main.mx-auto.grid.gap-6.w-full.grid-cols-1.lg:max-w-screen-2xl.lg:grid-cols-[minmax(0,1fr)_calc(var(--spacing)*64)].lg:px-2.xl:grid-cols-[calc(var(--spacing)*64)_minmax(0,1fr)_calc(var(--spacing)*64)].2xl:grid-cols-[calc(var(--spacing)*64)_minmax(0,calc(var(--spacing)*213))_calc(var(--spacing)*64)] > div.w-full > div.bg-article-background.p-4.shadow-2xs.md:p-6 > article > div.znc.mt-8 > p.code-line > a",
"match": "href=\"https://github.com/hatappo/agent-slash-sync\""
},
{
"selector": "main.mx-auto.grid.gap-6.w-full.grid-cols-1.lg:max-w-screen-2xl.lg:grid-cols-[minmax(0,1fr)_calc(var(--spacing)*64)].lg:px-2.xl:grid-cols-[calc(var(--spacing)*64)_minmax(0,1fr)_calc(var(--spacing)*64)].2xl:grid-cols-[calc(var(--spacing)*64)_minmax(0,calc(var(--spacing)*213))_calc(var(--spacing)*64)] > div.w-full > div.bg-article-background.p-4.shadow-2xs.md:p-6 > article > div.znc.mt-8 > p.code-line",
"match": "hatappo/agent-slash-sync をインストールします。"
},
...
]
記事本文が article > div.znc 配下にあることが分かります。テキストだけでなく属性値(href)にもマッチしています。また、2 回目のアクセスではキャッシュが使われている旨が告知されています。パース対象の URL は約 2 分間キャッシュされるため、セレクタの当たりを付ける試行錯誤で、相手のサイトに繰り返しリクエストを送らずに済みます。
Extract: 構造化データの抽出
構造が分かったら --row で複数フィールドをまとめて抽出します。このページのサイドバーに表示される目次は JavaScript で後から描画されるため生の HTML には存在しませんが、本文の見出し(h2.code-line / h3.code-line)はサーバーレンダリングされているので、ここから記事の目次を組み立ててみます。
$ ax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ \
'h2.code-line, h3.code-line' --row 'section=, anchor=@id'
ax: note: 4 rows extracted, no empty fields
section anchor
AI 支援型テスト駆動開発フレームワーク『Tsumiki』 ai-%E6%94%AF%E6%8F%B4%E5%9E%8B%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%80%8Etsumiki%E3%80%8F
カスタムスラッシュコマンド %E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%B9%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89
やってみた %E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F
さいごに %E3%81%95%E3%81%84%E3%81%94%E3%81%AB
ヘッダ 1 回の TSV で返ってきました。--row のフィールド定義では、空のセレクタはマッチした要素自身で @id のように @ を付けると属性値になります。
HTML のテーブルは --table でヘッダをキーにした行として抽出でき、さらに --where でフィルタもできます。弊社コーポレートサイトの会社概要から 3 項目だけ抜き出してみます。
$ ax https://classmethod.jp/company/ 'table' --table --where 'col0 ~ /設立|資本金|従業員数/'
ax: note: 3 rows extracted, no empty fields
col0 col1
設立 2004年7月7日
従業員数 約820名(2025年7月現在、グループ全体)
資本金 1億円
--where は eval を使わない安全な式言語で、price > 100 && name ~ /^foo/i のような条件が書けます。ヘッダ行のないテーブルだったため、col0、col1 という列名が自動で割り当てられているようです。
ドキュメントを Markdown で読む
--md を付けるとページ本文を Markdown に変換して返します。トークン予算を付けて先ほどの記事を読んでみます。
$ ax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ --md --budget 200
ax: note: using 34s-old cached fetch (--fresh to refetch)
ウィスキー、シガー、パイプをこよなく愛する大栗です。
弊社クラスメソッドでは AI 支援型テスト駆動開発フレームワークである Tsumiki を公開しています。Tsumiki は Claude Code での利用を前提としています。本エントリでは Gemini CLI で動かしてみようと思います。
[https://classmethod.jp/news/20250729-tsumiki/](https://classmethod.jp/news/20250729-tsumiki/)
## [](#ai-%E6%94%AF%E6%8F%B4%E5%9E%8B%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%80%8Etsumiki%E3%80%8F) AI 支援型テスト駆動開発フレームワーク『Tsumiki』
Tsumiki は AI 駆動開発のためのテスト駆動開発フレームワークです。特に Claude Code を使用して効率的な開発プロセスを要件定義から設計、タスク分割、実装まで体系化して提供します。
Tsumiki は包括的開発フローをサポートするための Kairo コマンド、テストケースとコード実装を個別に行う TDD コマンド、リバースエンジニアリングを行う Rev コマンドなどがあります。
ax: note: 201 more result(s) hidden (use --all, --limit N, or --budget T)
ナビゲーションや装飾が取り払われ、本文だけが Markdown で返ってきます。エージェントがリファレンスやブログ記事を読む用途にちょうど良く、--budget でコンテキストに入れる量を制御できるのが AI エージェント向けで実用的です。
Claude Code と連携してみる
最後に本命の Claude Code との連携です。公式の導入方法は npx skills add yusukebe/ax ですが、実体はリポジトリの skills/ax/SKILL.md をプロジェクトに配置するだけですが、今回は公式のコマンドを実行します。
$ npx skills add yusukebe/ax
SKILL.md の description には「URL の取得、未知のページの探索、HTML からの構造化データ抽出では curl + 書き捨てスクリプトの代わりに ax CLI を使うこと」という趣旨のトリガー条件が書かれており、エージェントがインラインスクリプトや素の curl を書こうとした瞬間に発動する設計です。
このプロジェクトで Claude Code で実行し、先ほどの記事の目次を抽出させてみます。
https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ の記事の見出し(目次)一覧を抽出してください
実行ログからツール呼び出しを抜粋すると、以下の流れになっていました。
- ax スキルが自動で発動する
- 書き捨てスクリプトを書かずに、まず見出しセレクタの仮説を立てて
--rowを試す h2に--textを使って見出しを抽出し、目次を回答する
このとき Claude Code が実行したコマンドの例は以下です。
ax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ 'h1, h2, h3, h4' --row 'tag=@tagName, text=' --all
ax https://dev.classmethod.jp/articles/tsumiki-an-ai-tdd-framework-with-gemini-cli/ 'article h2' --text; echo ---; ax https://dev.classmethod.jp/articles/tsumik…
最終的に「AI 支援型テスト駆動開発フレームワーク『Tsumiki』(h2)→ カスタムスラッシュコマンド(h3)→ やってみた(h2)→ さいごに(h2)」という正しい目次が返ってきました。
⏺ 記事の見出し(目次)は以下のとおりです。
記事タイトル(h1): AI 支援型テスト駆動開発フレームワークの Tsumiki を Gemini CLI で動かせるようにしてみた
┌────────┬──────────────────────────────────────────────────┐
│ レベル │ 見出し │
├────────┼──────────────────────────────────────────────────┤
│ h2 │ AI 支援型テスト駆動開発フレームワーク『Tsumiki』 │
├────────┼──────────────────────────────────────────────────┤
│ h3 │ └ カスタムスラッシュコマンド │
├────────┼──────────────────────────────────────────────────┤
│ h2 │ やってみた │
├────────┼──────────────────────────────────────────────────┤
│ h2 │ さいごに │
└────────┴──────────────────────────────────────────────────┘
本文の見出しは h2 が 3 つ、その最初のセクション配下に h3 が 1 つというシンプルな構成です(h4 以下はありません)。
Python スクリプトの生成も生 HTML のダンプも一切発生せず、仮説 → 構造確認 → 抽出という ax の想定ワークフローどおりに動いています。試行錯誤のフェッチはすべてキャッシュにヒットするため、相手サイトへのリクエストも最小限です。全体で約 40 秒 で完了しました。
さいごに
ax を一通り触ってみて、エージェントが Web ページを扱うときの「書き捨てスクリプトが構造変更で壊れる」「生 HTML でコンテキストが溢れる」という定番のつまずきが、まとめて解消されるのを実感できました。特に、省略や切り詰めを必ず告知し、エラーにも注記を添えるという出力設計は、人間向けというよりエージェント向けツールならではの発想です。
以前 Cloudflare Workers を利用して Web2MarkDown というツールを作成しましたが、API キー不要でローカルで動作する ax は素晴らしいなと思っています。
決定論的に動作するので、既存の環境に足すハードルは低いと思います。Claude Code をはじめとするコーディングエージェントをお使いの方は、スキルを 1 つ足すだけでトークン消費とコストの削減が見込めるので、まず試してみるのが良いのではないでしょうか。私も普段のエージェント環境に組み込んで、ドンドン使って行きたいと思います。








