Webスクレイピングの標準的なスタックは長らく「Node.js + Puppeteer + headless Chrome」だった。手軽で実績もあるが、起動に約2秒かかり、メモリを200MB以上消費し、Chromeバイナリ(300MB超)の配布が前提というのは、大規模な自動化や軽量なAIエージェントには少々重い。

2026年4月にv0.1.0をリリースしたObscura(★24,363・2026-09-03時点)はこの前提を崩す。Rustで書かれたヘッドレスブラウザエンジンで、V8エンジンを組み込んでJavaScriptを完全実行しながら、Chrome DevTools Protocol(CDP)を独自実装することでPuppeteer・Playwrightとの互換性を維持する。結果として起動時間は即時、ページ読み込みは85ms(Chromeの約6分の1)、メモリ使用量は30MB(Chromeの約15%)を達成した。

本記事では、ObscuraのCDPアーキテクチャ、ステルスモードの実装、CLIの基本操作、Puppeteer・Playwrightとの接続、そしてv0.2.1で入ったMCPサーバーまでを解説する。公称値をなぞるだけでなく、2026-09-03に公式配布バイナリ(v0.2.1・aarch64-macos)を実際に落として動かし、メモリ・MCPツール数・常駐トークン数を測った結果を併記する。

Obscura v0.2.1 を macOS で実行したターミナル記録。--version が 0.2.1 を返し、fetch がページタイトルを取得し、ピークRSSが25.1MB、MCPのtools/listが37ツールであることを示している
2026-09-03 に実機収録。公式配布の obscura-aarch64-macos.tar.gz を展開し、--versionfetch/ピークRSS計測を順に実行したところ。asciinema で録画し agg でGIF化した(この記事のための収録)。記録中の peak RSS: 25.1 MB/usr/bin/time -l の値を 1,048,576 で割った MiB 表記で、十進 MB に直すと 26.3 MB。本文と図で使っている 26.2 MB(3回計測の中央値・÷1,000,000)と同じ量を指している。
30秒でわかるObscura v0.2.1
・Chromeバイナリも Node.js も要らない単一のRustバイナリ。V8を内蔵してJSを実行する
obscura serve がCDPを話すので、既存のPuppeteer・Playwrightコードを接続先だけ変えて動かせる
・v0.2.1ではobscura mcpでMCPサーバーにもなる。実測でツールは37個(READMEの表は14個)
・実測ピークRSSは26.2MBで公称30MBの範囲内。待機中の serve は13.9MB
・MCPの常駐コストは4,895トークン(Anthropic count_tokens)。当サイトで測った9サーバー中1ツールあたり最安
Obscura v0.2.1 を手元で測った4つの数字。fetch時のピークRSS 26.2MB、serve待機時のRSS 13.9MB、MCP tools/list が返すツール数 37、本体バイナリ 90.1MB
この記事のために測った4つの数字。計測環境は Darwin 23.5.0 / arm64(M2・16GB)で、公式配布の obscura-aarch64-macos.tar.gz(v0.2.1)を展開して実行した。

AIエージェントにブラウザを持たせる設計全体の見取り図はAIエージェントフレームワーク比較2026|LangGraph・CrewAI・Dify等9種をStar数・実コードで検証にまとめている。

Obscuraとは:RustとV8で実装したPuppeteer互換ヘッドレスブラウザ

ObscuraはRust製のヘッドレスブラウザエンジンで、GitHubリポジトリ h4ckf0r0day/obscura でApache 2.0ライセンスの下で公開されている。コードベースはRust 100%で構成され、JavaScript実行エンジンとしてV8を組み込んでいる。

「ヘッドレスブラウザ」と「ヘッドレスChrome」の違い
ヘッドレスブラウザとはGUI(画面描画)なしで動作するブラウザエンジン全般を指す。「headless Chrome」はChromeブラウザをGUIなしモードで起動したものだが、Chromeのバイナリが必要で起動コストが高い。ObscuraはChromeバイナリを一切使わず、RustとV8で独立したブラウザエンジンを実装している。

なぜRustとV8を選んだのか

Rustを採用した理由はパフォーマンスとメモリ安全性にある。C++で書かれた多くのブラウザエンジンとは異なり、Rustはゼロコスト抽象化とメモリ安全性をコンパイル時に保証する。並列スクレイピング時のデータ競合や解放済みメモリへのアクセスを言語レベルで防げる。

V8(Google製のJavaScriptエンジン)を組み込むことで、モダンなJavaScriptを完全実行できる。これにより、React・Vue・Next.jsなどのSPA(シングルページアプリケーション)が動的に生成するDOMコンテンツも正確に取得できる。静的HTMLパーサーとは根本的に異なる。

Obscuraの3つの動作モード

Obscuraは用途に応じて3つのモードで動作する。

モード コマンド 用途 追加された版
CLIフェッチ obscura fetch <URL> 単一ページの取得・JS評価 v0.1.0
並列スクレイピング obscura scrape <URL...> 複数URLの並列処理 v0.1.0
CDPサーバー obscura serve Puppeteer/Playwright連携 v0.1.0
MCPサーバー obscura mcp AIエージェントへ直接ツールを渡す v0.2.x

初出時(v0.1.0)は上の3つだったが、v0.2.1 の obscura --help が列挙するサブコマンドは serve / fetch / scrape / mcp の4つである(実行して確認)。4つ目の mcp は後述の専用セクションで扱う。

CLIモードはシェルスクリプトやPythonパイプラインからの呼び出しに適している。CDPサーバーモードは既存のPuppeteer/Playwrightコードをほぼ無改修で動かせる。用途に応じて使い分けることで、スクレイピングパイプラインを段階的に移行できる。

fetch / serve / mcp の使い分け図。serveが向くのは既存のPuppeteer・Playwright資産をそのまま動かす場合、mcpが向くのはClaude Desktop・Cursorから直接ブラウザを触らせる場合
servemcp はどちらも同じエンジンを使う。分かれ目は「既存のCDPクライアント資産があるか」で、無いなら mcp のほうが配線が少ない。

ObscuraのCDP実装アーキテクチャ——Rust製ブラウザがChromeのプロトコルを話す仕組み

ObscuraがPuppeteer・Playwrightと互換性を持つ理由は、Chrome DevTools Protocol(CDP)を独自実装しているからだ。CDPはGoogleが策定したブラウザ制御プロトコルで、WebSocketを通じてブラウザの内部機能を操作する。

flowchart LR subgraph Client["クライアント"] Puppeteer["Puppeteer / Playwright
(既存スクリプト)"] CLI["obscura CLI
(シェル・Python)"] end subgraph Obscura["Obscura エンジン"] WS["WebSocket サーバー
ws://127.0.0.1:9222"] CDP["CDP ディスパッチャー"] subgraph Domains["CDP ドメイン"] Target["Target"] Page["Page"] Runtime["Runtime"] DOM["DOM"] Network["Network"] Fetch["Fetch"] Input["Input"] end V8["V8 JavaScript
エンジン"] Stealth["Stealth モジュール
(フィンガープリント
ランダム化)"] end Puppeteer -->|"WebSocket CDP"| WS CLI -->|"内部呼び出し"| CDP WS --> CDP CDP --> Domains Domains --> V8 V8 --> Stealth

実装済みのCDPドメイン

Obscura v0.1.0が実装するCDPドメインは以下の通りだ:

Target ドメインcreateTargetcloseTargetattachToTargetcreateBrowserContextdisposeBrowserContext を実装。複数タブ・複数ブラウザコンテキストの管理ができる。

Page ドメインnavigategetFrameTreeaddScriptToEvaluateOnNewDocumentlifecycleEvents を実装。ページナビゲーションの制御と、ページ読み込みのライフサイクルイベント(loaddomcontentloadednetworkidle0)を検出できる。

Runtime ドメインevaluatecallFunctionOngetPropertiesaddBinding を実装。ページコンテキストでJavaScriptを実行し、結果を取得する中核機能だ。

DOM ドメインgetDocumentquerySelectorquerySelectorAllgetOuterHTMLresolveNode を実装。DOM要素へのアクセスとHTMLの取得を可能にする。

Network ドメインenablesetCookiesgetCookiessetExtraHTTPHeaderssetUserAgentOverride を実装。HTTPヘッダーのカスタマイズやCookieの管理ができる。

Fetch ドメインenablecontinueRequestfulfillRequestfailRequest を実装。リクエストのライブインターセプトが可能で、レスポンスのモック・書き換えができる。

Storage ドメイン:Cookie・localStorage等のストレージへのアクセスを実装。ログイン状態の維持・セッション管理に使う。

Input ドメインdispatchMouseEventdispatchKeyEvent を実装。マウスクリックやキーボード入力をシミュレートする。

LP ドメインgetMarkdown を実装。DOMをMarkdownに変換するカスタムメソッドで、LLMへのコンテキスト供給に直接活用できる。

LP.getMarkdown:LLMとの連携に便利なカスタム拡張
標準CDPには存在しないLP(Language Processing)ドメインの getMarkdown メソッドは、Obscura独自の拡張だ。ページのDOM構造をMarkdown形式で返すため、取得したコンテンツをClaude・GPT-4等のLLMに渡す際にトークン数を削減できる。AIエージェントがWebページを「読む」用途に最適化された設計だ。

Obscuraのインストールと基本コマンド(Linux・macOS・Windows・Docker)

バイナリのインストール

Obscuraはプリコンパイル済みバイナリをリリースページで配布している。Rustのインストールなしに動作する。

# Linux x86_64
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-linux.tar.gz
tar xzf obscura-x86_64-linux.tar.gz
./obscura fetch https://example.com --eval "document.title"

# macOS Apple Silicon
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-aarch64-macos.tar.gz
tar xzf obscura-aarch64-macos.tar.gz

# macOS Intel
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-macos.tar.gz
tar xzf obscura-x86_64-macos.tar.gz

ソースからのビルド(ステルスモード有効化)

ステルスモードを含めてビルドする場合はCargoを使う。初回ビルドではV8をソースからコンパイルするため約5分かかるが、以降はキャッシュが効く。

git clone https://github.com/h4ckf0r0day/obscura.git
cd obscura
cargo build --release

# ステルスモード(フィンガープリントランダム化+トラッカーブロック)を有効にしてビルド
cargo build --release --features stealth

Rust 1.75以上が必要で、rustupから入手できる。

obscura fetch:ページ取得の基本

obscura fetch はURLを指定してページを取得するコマンドだ。

# ページタイトルをJSで取得
obscura fetch https://example.com --eval "document.title"

# ページ内の全リンクを取得
obscura fetch https://example.com --dump links

# 完全なHTMLを出力
obscura fetch https://news.ycombinator.com --dump html

# networkidle0まで待機してからスクレイピング(SPAに有効)
obscura fetch https://example.com --wait-until networkidle0

# ステルスモードで取得(フィンガープリントランダム化+トラッカーブロック)
obscura fetch https://example.com --stealth --eval "document.title"

--wait-until フラグは3段階の待機条件を指定できる:load(デフォルト)、domcontentloadednetworkidle0(ネットワーク通信が一定時間ない状態)。React・Next.js等のSPAでは networkidle0 を使うことで動的コンテンツが生成された後のDOMを取得できる。

obscura scrape:並列スクレイピング

obscura scrape は複数URLを並列処理するコマンドだ。

obscura scrape \
  https://example.com/page1 \
  https://example.com/page2 \
  https://example.com/page3 \
  --concurrency 25 \
  --eval "document.querySelector('h1').textContent" \
  --format json

--concurrency でワーカー数を指定できる。デフォルトは10で、最大値はシステムリソースに依存する。--format json を指定するとURL・取得結果・タイムスタンプを含むJSONを出力する。パイプラインやPythonスクリプトへの入力として扱いやすい。

PuppeteerとPlaywrightをObscuraに接続する(CDPサーバーモード)

obscura serve はWebSocketサーバーを起動し、Puppeteer・Playwrightからの接続を受け付けるモードだ。既存のスクレイピングコードをほぼ無改修でObscuraに移行できる。

# 基本起動(ポート9222でWebSocket待機)
obscura serve --port 9222

# ステルスモード有効化
obscura serve --port 9222 --stealth

# 並列ワーカー数を指定
obscura serve --port 9222 --workers 4 --stealth

Puppeteerから接続する

CDPサーバーが起動したら、Puppeteerの connect() でWebSocketエンドポイントに接続する。puppeteer.launch() ではなく puppeteer.connect() を使う点が既存コードとの唯一の違いだ。

import puppeteer from 'puppeteer-core';

const browser = await puppeteer.connect({
  browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser',
});

const page = await browser.newPage();
await page.goto('https://news.ycombinator.com');

const stories = await page.evaluate(() =>
  Array.from(document.querySelectorAll('.titleline > a'))
    .map(a => ({ title: a.textContent, url: a.href }))
);
console.log(stories);

await browser.disconnect();

puppeteer-core を使うことでChromeバイナリのバンドルを避けられる。browserWSEndpoint をObscuraのWebSocketアドレスに向けるだけで既存のPuppeteerコードがObscura上で動作する。

Playwrightから接続する

PlaywrightはChromium・Firefox・WebKitを統一APIで操作するフレームワークだが、CDPエンドポイントへの接続もサポートしている。

import { chromium } from 'playwright-core';

const browser = await chromium.connectOverCDP({
  endpointURL: 'ws://127.0.0.1:9222',
});

const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://en.wikipedia.org/wiki/Web_scraping');
console.log(await page.title());

await browser.close();

chromium.connectOverCDP() にObscuraのWebSocketエンドポイントを渡す。PlaywrightのAPIはPuppeteerよりも抽象度が高く、page.locator()page.fill()page.click() 等の高レベルAPIがそのまま使える。

ログイン自動化とセッション管理

フォーム送信・ログイン・Cookie維持もサポートしている。

const page = await browser.newPage();
await page.goto('https://quotes.toscrape.com/login');
await page.evaluate(() => {
  document.querySelector('#username').value = 'admin';
  document.querySelector('#password').value = 'admin';
  document.querySelector('form').submit();
});
// Obscuraはフォーム送信後の302リダイレクトを追跡し、Cookieを維持する

ログイン後のCookieはセッション間で Storage.getCookiesStorage.setCookies を通じて保存・復元できる。これにより、同一アカウントで複数のスクレイピングセッションを効率よく実行できる。

obscura mcp:MCPサーバーとしてのObscuraを実測する(37ツール・常駐4,895トークン)

v0.2.1 で増えた4つ目のサブコマンドが obscura mcp だ。stdio で Model Context Protocol を話すサーバーとして起動し、Claude Desktop や Cursor などのMCPクライアントから、CDPクライアントを自前で書かずにブラウザを操作させられる。

# Claude Desktop / Cursor 等の設定に書く場合のコマンド
obscura mcp

# ステルスを有効にしたまま MCP を提供する(--stealth はグローバルオプション)
obscura --stealth mcp

READMEの表は14件、実際に返るのは37件

MCPサーバーが接続時に返す tools/list を、stdio へ initializenotifications/initializedtools/list の順に直接投げて数えた。

obscura mcp を stdio で起動して tools/list を実測した結果。常駐トークンは cl100k_base で3,355、Anthropic count_tokens で4,895。ツールはナビゲーション系7・操作系8・抽出系9などで合計37件
2026-09-04 に再計測(2026-09-03 の初回計測と同値)。tools/list の応答は 13,234 バイトで、ツール定義は37件。READMEの Tools 表に載っているのは14件で、残り23件は文書化されていない。

READMEの Tools 表に列挙されているのは14件browser_navigate / browser_snapshot / browser_screenshot / browser_pdf / browser_click / browser_fill / browser_type / browser_press_key / browser_select_option / browser_evaluate / browser_wait_for / browser_network_requests / browser_console_messages / browser_close)だが、実際に tools/list が返すのは37件だった。差分の23件は次のとおりで、いずれもREADMEに記載がない。

系統 READMEに無いツール
ページ遷移 browser_back / browser_forward / browser_reload
Cookie・ストレージ browser_get_cookies / browser_set_cookie / browser_clear_cookies / browser_storage_state / browser_set_storage_state
フォーム browser_detect_forms / browser_fill_form
抽出 browser_markdown / browser_links / browser_interactive_elements / browser_extract / browser_get_attribute / browser_count / browser_search
待機・移動 browser_wait_for_text / browser_scroll
タブ操作 browser_tab_new / browser_tab_list / browser_tab_switch / browser_tab_close

とくに browser_markdown は、CDP側の独自拡張 LP.getMarkdown に対応するMCPツールで、ページ本文をMarkdownにしてLLMへ渡す用途に直結する。READMEだけを読んで機能を判断すると、この記事で最も価値のある機能を見落とすことになる。

常駐コストは4,895トークン——測った9サーバー中いちばん安い

MCPサーバーは、ツールを1つも呼ばなくても接続しているだけで tools/list のツール定義がコンテキストに載る。これが毎セッション掛かる固定費になる。当サイトで同じ物差し(tools/list の result オブジェクトを再シリアライズしたバイト列)で測ってきたサーバーと並べると次のようになった。

サーバー ツール数 バイト トークン 1ツールあたり
OpenKnowledge MCP 0.66.2 21 153,900 58,460 2,784
Notion MCP (local) 2.5.1 24 81,951 33,459 1,394
draw.io MCP 1.5.0 7 54,190 24,760 3,537
Backlog MCP 0.18.0 62 42,363 16,555 267
Blender MCP 1.8.7 25 25,678 9,743 390
Supabase MCP (local) 0.11.0 29 22,117 8,585 296
Chrome DevTools MCP 1.8.0 29 25,806 6,561 311
Obscura MCP v0.2.1 37 13,234 4,895 132
Microsoft Learn MCP remote 3 5,012 1,810 603

トークナイザは Anthropic の count_tokens で統一している。同じ入力を cl100k_base(tiktoken)で数えると Obscura は 3,355トークンになり、1.46倍ずれる。トークン数を比較するときはトークナイザ名を必ず併記しないと、この倍率ぶんの誤差が入る。

比較して意味があるのは同じ用途の Chrome DevTools MCP との対比だ。Obscuraのほうがツールは8個多い(37 vs 29)のに、常駐トークンは25%少ない(4,895 vs 6,561)。1ツールあたりでは 132 対 311 で、2.4分の1になる。常駐コストを決めているのはツールの個数ではなく引数スキーマの複雑さで、Obscuraのツールは引数が url / selector / text 程度の素直な形に揃っている。

MCPサーバー版は 0.1.0 を名乗る
initialize の応答に入る serverInfo{"name":"obscura-mcp","version":"0.1.0"} で、CLI本体の --version(0.2.1)とは別系統のバージョンが振られている。プロトコル版は 2024-11-05 を宣言する。MCPクライアント側でバージョンを見て分岐している場合は、CLIのタグと一致しない点に注意。

CDPサーバーが名乗る UA は macOS 上でも「Linux」

obscura serve を起動して /json/version を叩くと、次のように返る(実行して確認)。

obscura serve -p 9333 &
curl -s http://127.0.0.1:9333/json/version

返ってきた BrowserChrome/145.0.0.0V8-Version14.5.0.0 で、Puppeteer・Playwright から見れば普通のChromeに見える。ただし既定の User-AgentMozilla/5.0 (X11; Linux x86_64) ... Chrome/145.0.0.0 Safari/537.36 で、macOS 上で動かしていても Linux を名乗る。OSとUAの不一致はボット検出の手掛かりになりうるので、ステルス用途では --user-agent で実行環境に合わせて上書きしておくほうがよい。

ステルスモードとフィンガープリントランダム化の技術的仕組み

Obscuraのステルスモードは --stealth フラグ(またはビルド時 --features stealth)で有効化される。現代のボット検出システムに対してどのように対応しているかを見ていく。

ステルスモードは合法的・倫理的な用途に限定して使用すること
検出回避そのものを主目的に設計されたOSSとしては[camofox-browser完全解説:C++偽装でAIエージェントの検出を回避するREST APIブラウザ](/agent/camofox-browser-stealth-rest-api-ai-agent/)があり、Obscuraの「本体に同梱された副次機能としてのステルス」とは設計思想が異なる。 ステルスモードはWebサービスの利用規約、著作権法、不正アクセス禁止法に反する用途には使用してはならない。自社サービスのテスト、research目的の学術利用、利用規約でスクレイピングが許可されているサービスへの適用など、明示的に許可された用途のみに使うこと。Cloudflareが推進する Web Bot Auth(HTTPメッセージ署名)のように、正規のAIエージェントが自己証明するための仕組みも普及しつつある。

フィンガープリントランダム化の対象

ヘッドレスブラウザがChromeと異なる点は複数ある。ObscuraはこれらをセッションごとにランダムなChromeらしい値で埋める。

navigator.webdriver の除去

通常のヘッドレスブラウザでは navigator.webdriver === true になる。これはbot検出の最も基本的なシグナルだ。Obscuraはこのプロパティを undefined に設定し、通常のChromeと同一の挙動にする。

Function.prototype.toString() のマスキング

JavaScriptで関数を文字列化すると、ネイティブ実装の場合は function () { [native code] } と表示される。Puppeteerが注入するカスタム関数は通常のJS関数として見えてしまうため、これを検出するサイトがある。Obscuraは注入関数の .toString() もネイティブコード形式に偽装する。

Object.keys(window) の安全化

Puppeteerが自動的にwindowオブジェクトに追加するプロパティ($, $$, $$eval 等)は Object.keys(window) で検出できる。Obscuraはこれらの内部プロパティを隠蔽し、通常のChromeのwindow enumeration結果と一致させる。

イベントの isTrusted フラグ

スクリプトがプログラム的に生成したマウス・キーボードイベントは event.isTrusted === false になる。多くのbot検出スクリプトはこれを監視する。Obscuraは dispatchMouseEventdispatchKeyEvent で生成したイベントの isTrusted フラグを true に設定する。

ハードウェア情報のランダム化

情報の種類 説明
GPU UNMASKED_RENDERER_WEBGL / UNMASKED_VENDOR_WEBGLをセッションごとにランダムな本物のGPU名に設定
スクリーン screen.widthscreen.heightscreen.colorDepthを現実的な解像度でランダム化
Canvas Canvasフィンガープリントを検出不能なレベルでノイズ注入
Web Audio AudioContextのフィンガープリントをランダム化
Battery navigator.getBattery()の返す充電状態・残量をランダム化

navigator.userAgentData の設定

navigator.userAgentData.brandsuaFullVersion等のhigh-entropy値を、Chrome 145互換の現実的な値に設定する。これにより User-Agent Client Hints を使ったbot検出も回避できる。

3,520トラッカードメインのブロック

ステルスモードでは3,520のトラッカードメインからのリソース読み込みをブロックする。これはGoogle Analytics・Mixpanel・DoubleClick等のアナリティクス、広告ネットワーク、テレメトリサービス、そしてfpjs(FingerprintJS)のようなフィンガープリンティングライブラリを含む。

トラッカーをブロックすることには二重の効果がある。フィンガープリンティングスクリプト自体の読み込みを防ぐという直接的な回避効果に加え、不要なリソース読み込みをカットするためページ読み込み速度も向上する。

ベンチマーク:Obscuraとheadless Chromeの速度・メモリを実測で比べる

ObscuraはChromeが持つ多くの機能(PDF印刷、拡張機能、WebGL等)を省略し、自動化に必要な機能に特化している。その結果として生まれたパフォーマンス差は顕著だ。

xychart-beta title "ページ読み込み時間比較 (ms):棒=Obscura / 折れ線=Headless Chrome" x-axis ["静的HTML", "JS重いコンテンツ", "動的スクリプト", "平均"] y-axis "ミリ秒" 0 --> 600 bar [51, 84, 78, 85] line [210, 450, 380, 500]

数値で見るObscura vs headless Chrome

指標 Obscura Headless Chrome
ページ読み込み(静的HTML) 51 ms ~210 ms 約4.1倍高速
ページ読み込み(JS重い) 84 ms ~450 ms 約5.4倍高速
ページ読み込み(動的スクリプト) 78 ms ~380 ms 約4.9倍高速
平均ページ読み込み 85 ms ~500 ms 約5.9倍高速
起動時間 即時 ~2秒 -
メモリ使用量 30 MB 200+ MB 約6.7分の1
バイナリサイズ 70 MB 300+ MB 約4.3分の1

これらの数値は公式READMEに記載されているベンチマーク結果だ。環境やページの複雑度によって変動するが、傾向として起動オーバーヘッドがゼロであることと、不要なChromeコンポーネントを省いた軽量実装によるものだ。

headless Chromeを前提にしたスタックとObscura v0.2.1の比較図。左はChromeバイナリ300MB超・起動約2秒・メモリ200MB超、右は単一バイナリ・起動即時・メモリ公称30MBで実測26.2MB
左列はREADMEが比較対象として挙げる headless Chrome の値。右列のうち「26.2MB 実測」だけがこの記事の計測値で、残りはREADMEの公称値。

公称値のうち手元で確かめられた2つと、ズレていた1つ

上の表はREADMEの公称値であり、この記事の計測ではない。実際に v0.2.1 の配布バイナリで確かめられる項目だけを切り出すと、次のようになった。

項目 READMEの公称 手元の実測(Darwin 23.5.0 / arm64) 判定
メモリ使用量 30 MB fetch のピークRSS 26.2 MB(09-03の3回計測の中央値/09-04の再計測は 24.5・26.4・26.4 MB で中央値 26.4 MB) 公称の範囲内
起動時間 即時 fetch https://example.com が2回目以降 0.25〜0.39秒(DNS・HTTP込み) 概ね一致
バイナリサイズ 70 MB 180.7 MBobscura 94.5 MB + obscura-worker 86.2 MB) ズレている

バイナリサイズだけが公称と食い違う。macOS arm64 の公式リリース obscura-aarch64-macos.tar.gz(ダウンロード 75.9 MB)を展開すると、入っているのは1本ではなく obscuraobscura-worker2本で、合計は 180,690,080 バイトある。README の「70 MB」は片方だけを指しているか、別プラットフォームの値と思われる。

ただし主張の向きは変わらない——Chrome の 300MB 超に対して展開後 180MB、ダウンロードは 75.9 MB なので、依然として小さい。誇張されているのは倍率であって方向ではない。

ページ読み込み時間の 51〜85 ms は検証していない
なおメモリと起動時間の実測は日をまたいで2回行っている。ピークRSSは 2026-09-03 の3回で中央値 26.2 MB、2026-09-04 の3回で 24.5 / 26.4 / 26.4 MB(中央値 26.4 MB)。本文と図が使っている 26.2 MB は前者の値で、後者もほぼ同じところに落ちた。起動時間 0.25〜0.39 秒は 09-04 の計測である。
上の表の読み込み時間はREADMEの値をそのまま載せている。手元の fetch は外部ネットワークを含めて 0.25〜0.39 秒だったが、これはDNS解決とHTTP往復を含む数字で、READMEの「ページ読み込み」がどこからどこまでを測ったのかが公開されていないため、同じ土俵での比較になっていない。この記事では読み込み時間を「検証済み」とは書かない。
起動コストの削除が並列処理で特に効く
Chromeの起動2秒というコストは、1000URLを処理するバッチジョブで顕著に響く。単一プロセスを再利用すれば問題ないが、コンテナ環境でインスタンスを動的にスケールする場合は起動コストが積み重なる。Obscuraは起動が即時のため、オートスケーリング環境でのコールドスタートコストがゼロになる。

AIエージェント自動化での活用パターン

ObscuraのCDP互換性と軽量性は、AIエージェントがWebを操作するワークフローに特に適している。エージェント側にブラウザ操作のループごと持たせる設計はBrowser-Useの使い方|AIに実ブラウザを操作させるOSSの仕組み・導入・他ツール比較【2026】で扱っており、Obscuraはその下層で動く「ブラウザそのもの」を差し替える位置にある。agent-browserのようなAIエージェント専用ブラウザ自動化ツールと同様に、ObscuraもAIエージェントの「目と手」として機能する。

LLMにWebコンテキストを供給する

Obscuraの LP.getMarkdown カスタムCDPメソッドは、取得したページをMarkdown形式に変換してLLMへの入力に適した形式で返す。HTMLのタグを除去しながら見出し・リスト・リンクの構造を保持するため、コンテキストとして渡した際のトークン消費を抑えられる。

const browser = await puppeteer.connect({
  browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser',
});
const page = await browser.newPage();
await page.goto('https://example.com/article');

// Obscura独自の LP.getMarkdown でページをMarkdown化
const client = await page.createCDPSession();
const { markdown } = await client.send('LP.getMarkdown');
// → LLMへのプロンプトにそのまま渡せるMarkdown文字列

await browser.disconnect();

並列AIエージェントによる大規模リサーチ

obscura serve --workers 4 で複数ワーカーを立ち上げ、異なるURLを並列処理するAIエージェントパイプラインを構築できる。

# 4ワーカーのCDPサーバーをステルスモードで起動
obscura serve --port 9222 --workers 4 --stealth
import asyncio
from pyppeteer import connect

async def scrape_page(semaphore, url):
    async with semaphore:
        browser = await connect(browserURL='http://127.0.0.1:9222')
        page = await browser.newPage()
        await page.goto(url)
        content = await page.evaluate('document.body.innerText')
        await page.close()
        return {'url': url, 'content': content}

async def main(urls):
    semaphore = asyncio.Semaphore(10)  # 同時接続数制限
    tasks = [scrape_page(semaphore, url) for url in urls]
    return await asyncio.gather(*tasks)

スクレイピングパイプラインのコンテナデプロイ

Obscuraは単一バイナリ(70MB)のため、Dockerイメージへの組み込みが簡単だ。Chromeをインストールするよりも大幅にイメージサイズを削減できる。

FROM rust:1.75-alpine AS builder
WORKDIR /app
COPY . .
RUN cargo build --release --features stealth

FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/target/release/obscura /usr/local/bin/
EXPOSE 9222
CMD ["obscura", "serve", "--port", "9222", "--stealth"]

Chromeを含むPuppeteer用Dockerイメージが一般的に1GB以上になるのと比較して、Obscuraベースのイメージは大幅にスリムになる。

セキュリティと倫理的なスクレイピングの考慮事項

ステルスモードを持つ自動化ツールを使う際には、法的・倫理的な考慮事項を理解しておく必要がある。

Webサービスの利用規約とrobots.txt

obscura serve--obey-robots フラグをサポートしており、これを有効にすることでrobots.txtを尊重した動作が可能だ。robots.txtはWebサイトがクローラーに対して「取得してはいけないページ」を指定する標準的な仕組みだ。

法的観点では、利用規約でスクレイピングを禁じているサービスへの自動化アクセスは、国・地域によっては不正アクセスとみなされる場合がある。対象サービスの利用規約を確認してから使うことが前提だ。

ボット検出との「軍拡競争」

CloudflareのPrivacy Pass・Web Bot Authなどの次世代ボット認証技術が普及しつつある中で、フィンガープリントの回避だけでは正規アクセスを証明できなくなっていく。正規の自動化ツールであれば、将来的にはHTTPメッセージ署名(RFC 9421)による自己証明を組み込むことが求められるようになる可能性がある。

Obscuraのステルスモードは防御テスト・研究用途・許可されたスクレイピングのための機能であり、利用規約に反するアクセスを助長するためのものではない。開発者が自社サービスのbot検出品質を検証するためのレッドチームツールとして、あるいはAIエージェントのテスト環境として活用するのが本来の用途だ。

レート制限とサーバー負荷への配慮

--concurrency 25 のような高い並列数を指定する場合、対象サーバーへの負荷を考慮すること。礼儀として適切なウェイトを挟む設計が求められる。

// リクエスト間に遅延を挟む例
async function politeScrap(urls) {
  const results = [];
  for (const url of urls) {
    results.push(await scrapPage(url));
    await new Promise(r => setTimeout(r, 1000)); // 1秒待機
  }
  return results;
}

Rust製ヘッドレスブラウザの選択肢とObscuraの位置づけ

ヘッドレスブラウザツールはここ数年で多様化している。Obscuraがどこに位置するかを整理する。

ツール 言語 CDP互換 ステルス 特徴
Puppeteer Node.js Chrome CDPネイティブ 設定次第 Chromeバイナリ必要
Playwright Node.js/Python/C# Chrome/Firefox/WebKit 設定次第 マルチブラウザ対応
Obscura Rust 独自CDP実装 組み込み Chrome不要・軽量
Selenium 複数 WebDriver 設定次第 古いが広く使われる
Splash Python/Lua 独自 なし Scrapy連携特化
Ferrum Rust Chrome CDP なし Chromeバイナリ必要

ObscuraはRust製でChrome不要という点で独自のニッチを持つ。Ferrumも同じくRust製だが、ChromeのCDPをクライアントとして使うため実際のChromeバイナリが必要だ。ObscuraはChromeバイナリなしに完全な自己完結ができる唯一のRust製ヘッドレスブラウザエンジンだ。

既存のPuppeteer・Playwrightコードを持つチームが、Chromeバイナリを排除してコンテナイメージを軽量化したい、またはChromeの起動コストを解消したいというユースケースに特に適している。

まとめ:Obscuraが解決する具体的な課題

Obscura v0.1.0(Apache 2.0)は以下の課題を持つプロジェクトに向いている。

Obscuraが特に有効な3つのシナリオ

① コンテナ・サーバーレス環境:Chromeバイナリ不要でDockerイメージが軽量になり、コールドスタートが即時になる。

② 大規模並列スクレイピング:起動コストゼロ・メモリ30MBのため、同一マシンでChromeより多くのワーカーを動かせる。

③ AIエージェント組み込み:LP.getMarkdownカスタムCDPメソッドでページをLLM向けMarkdownに変換できる。既存のPuppeteer/Playwrightコードはほぼ無改修で動作する。

v0.1.0はまだ実験的なリリースだが、公開直後に2,400スターを集めた初動は開発者コミュニティの高い関心を示していた。その後もスターは伸び続け、2026-08-18時点で21,535(公開時の約9倍)、最終コミットは2026-08-15と開発も止まっていない。CDPの対応範囲が広がれば、軽量ヘッドレスブラウザの選択肢として本格的に普及する可能性がある。

参照ソース