Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Mar 1, 2026 - 日記

web2cli: Every website is a Unix command - ブラウザ自動化の前に「HTTPだけで済む仕事」を片付ける

web2cli cover image

Webから情報を取る方法って、極端に寄りがちです。

  • API(Application Programming Interface: 提供元が用意する機械向けの窓口)があるサイトはAPIを叩く
  • APIがない/高い/制限がキツいと、ブラウザ自動化(Playwrightなど)に寄る

でも、ブラウザ自動化は重い。 特にAIエージェント(AI agent: LLMが外部ツールを使って作業する仕組み)に組み込むと、遅い・高い・壊れやすいの三拍子になりがちです。

そんな中で見つけたのが、web2cli です。 コンセプトはREADMEに書かれている通りで、かなり強い。

“Every website is a Unix command.”

ここで言うUnixコマンド(Unix command: 標準入力/標準出力でつなげられる、いわゆるCLIコマンドの作法)としてWebを扱えるようにする、という話です。

web2cliとは?(雑に言うと「WebをCLIに落とす」)

web2cliは、Hacker News、Reddit、X.com(旧Twitter)、Discord、Slack、Stack Overflowなどを、ターミナルから叩けるCLIです。

基本のインターフェイスはこれ。

1
web2cli <site> <command>

README上の説明だと、内部は「軽量なWebブラウザ」ですが、いわゆるブラウザ自動化ではなく、基本はHTTPリクエストで処理します。 その上で、認証(auth: ログインやトークン注入)、セッション(session: Cookieやトークンを保持する状態)、簡易な対策回避などを吸収し、エージェントが扱いやすい構造化データを返す、という立て付けです。

オープンソースで、ライセンスはApache-2.0です。

これ、何がユニーク?

個人的に刺さったポイントは「ブラウザ自動化の前に、HTTPだけで済む仕事を片付ける」姿勢です。

ブラウザ自動化は万能ですが、

  • 起動が遅い(秒オーダーになりがち)
  • メモリを食う
  • DOMをパースするだけでトークンも増える(LLMに渡すと高い)

という問題があります。

web2cliはここに対して、少なくともREADME上は明確に「HTTP GET, not Chromium」「50ms not 5s」という路線で勝負しています。 この“割り切り”が一番価値だと思います。

まずは動かす(インストールと動作確認)

READMEに従うとインストールはこれです。

1
pip install web2cli

入ったか確認します。

1
web2cli --version

次に、Hacker Newsのトップを1件取ってみます。

1
web2cli hn top --limit 1

ここまで動けば、最低限のルートは通っています。

実用例1: HNをさっと一覧にする

READMEの例がそのまま良いです。

1
web2cli hn top --limit 3

--formatで出力形式を変えられます(README例ではJSONやMarkdownが出ています)。

1
web2cli hn top --limit 3 --fields title,url --format md

この時点で、ブラウザ自動化に比べて「LLMに渡す前の整形」がかなり楽になります。 HTMLをそのまま投げて“良きに計らう”より、最初から表やJSONに寄せた方が、だいたい安いしブレません。

実用例2: X検索をログイン込みで扱う

web2cliは、サイトによってはブラウザログイン経由でセッションを取得します。 READMEの例はこんな感じ。

1
web2cli login x --browser

ログイン後の検索はこう。

1
web2cli x search --query "build for agents" --limit 1 --format json

README上は、取得したセッションを ~/.web2cli/sessions/<domain>.json.enc に暗号化して保存すると書かれています。 (この設計は、運用的にかなり重要です。後で触れます)

実用例3: Discordに投げる(通知の素朴な足回り)

「CLIの出力をどこかに送る」は、地味に強いです。

READMEにはDiscord送信が載っています。

1
2
web2cli login discord --browser
web2cli discord send --server "My Server" --channel general --message "deployed" > /dev/null

たとえば、HNの上位を毎朝Discordに流す、みたいな運用が簡単に書けます。 (READMEの例はさらに、LLMの要約を挟んでから投げています)

「アダプタ」という設計が、いちばん“エージェント向き”

web2cliは、サイトごとにアダプタ(adapter: サイト固有のログイン方法やエンドポイント、抽出ルールをまとめたもの)を持ちます。

ここで言う「組み込みのアダプター」は、web2cli本体に最初から入っている公式アダプターのことです。 READMEの時点だと、少なくとも以下が用意されています(コマンド名は web2cli <site> <command><site> / <command> です)。

組み込みアダプター一覧(README記載ベース)

  • discord.comdc, discord
    • me, servers, channels, messages, send
    • dm, dm-messages, dm-send
  • news.ycombinator.comhn
    • top, new, item, search
    • saved, upvoted(要ログイン)
    • submissions
  • reddit.comreddit
    • posts, thread, search
  • slack.comslack
    • me, channels, messages, send
    • dm, dm-messages, dm-send
  • stackoverflow.comso
    • search, question, tagged
  • x.comx, twitter
    • tweet, profile, search, timeline, following

アダプタ一覧/詳細はCLIから確認できます。

1
2
web2cli adapters list
web2cli adapters info <domain-or-alias>

さらに、カスタムアダプタはYAMLで作れると書かれています。 これは「サイトごとのスクレイピングコードが増殖して地獄になる」問題に、かなり正面から向き合っている印象です。 (仕様は docs/adapter-spec.md にあります)

アダプターの書き方(最短ルート)

ここから先は「で、どうやってアダプターを書くの?」の話です。 一次情報はこの2つです。

やることはシンプルで、~/.web2cli/adapters/<domain>/web2cli.yaml を作って、validatelint を通すだけです。

1. 練習用アダプターを1本作る(httpbin.org)

目的: “web2cli ” が自分のアダプターで動く状態を作ります。

  1. ディレクトリを作ります。
1
mkdir -p ~/.web2cli/adapters/httpbin.org
  1. ~/.web2cli/adapters/httpbin.org/web2cli.yaml を作ります(quickstart掲載の最小構成です)。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
meta:
  spec_version: "0.2"
  name: httpbin
  domain: httpbin.org
  base_url: https://httpbin.org
  version: 0.2.0
  description: "HTTPBin demo adapter"
  author: custom
  aliases: [hb]
  transport: http
  impersonate: chrome
  default_headers:
    Accept: "application/json"

commands:
  ip:
    description: "Show IP seen by server"
    pipeline:
      - request:
          name: fetch
          method: GET
          url: /ip
      - parse:
          name: parsed
          from: fetch
          format: json
          extract: "$"
          fields:
            - name: origin
              from: "$.origin"
    output:
      from_step: parsed
      default_fields: [origin]
      default_format: table
  1. 構造チェックと意味チェックを通します。
1
2
web2cli adapters validate
web2cli adapters lint httpbin.org
  1. 実行して確認します。
1
2
web2cli adapters info hb
web2cli hb ip

もし動かなければ、--trace を付けると、どのステップで詰まっているか見えます。

1
web2cli hb ip --trace --verbose

2. 「ログインが必要」なサイトは auth を書く

ログインが絡むサイトは、リクエストにCookieやトークンを注入する必要があります。 web2cliは、アダプター側で auth(auth: 認証情報の注入ルール)を定義できます。

例えば、トークンを Authorization ヘッダに入れる形ならこんな感じです。

1
2
3
4
5
6
7
8
auth:
  methods:
    - type: token
      env_var: WEB2CLI_EXAMPLE_TOKEN
      inject:
        target: header
        key: Authorization
        prefix: "Bearer "

さらに web2cli login <domain> --browser のフローで、ブラウザの通信からトークンを捕まえる(captureする)設定も書けます。 このへんは spec の auth セクションが一番正確です。

注意: “便利”と“やっていい”は別です

ここは記事として外せないので先に書きます。

web2cliは「No browser, no API keys」と言っていますが、どのみちWebを叩く以上、

  • 利用規約(Terms of Service)
  • robots.txt
  • アカウントの扱い(ボット利用が許されるか)

は避けられません。

特にログイン系(DiscordやXなど)は、組織でやると一瞬で事故ります。 まずは個人用途の範囲で、ログを残し、実行頻度を絞って試すのが安全です。

まとめ: ブラウザ自動化を“最後の手段”に戻せるかもしれない

web2cliの良さは、機能そのものよりも、前提の置き方にあると思います。

  • ブラウザを起動せず、HTTPで済む作業はHTTPで片付ける
  • エージェントに渡すのは、HTMLではなく「構造化された最小の情報」
  • サイト差分はアダプタに閉じ込め、インターフェイスは web2cli <site> <command> に寄せる

自分はこれ、個人の自動化よりも「エージェントの足回り」に効く道具だと感じました。

次のアクション案:

  1. まずは web2cli hn top --limit 3 で“体感”を掴む
  2. web2cli adapters list で、何がどこまで用意されているか眺める
  3. 自分が欲しいサイトがあるなら、YAMLアダプタの導線(spec)を読む

参考リンク