ローカルAPIでブラウザプロファイルを自動化する

スクリプトからプロファイルを起動してデバッグ用エンドポイントを受け取り、アイデンティティとプロキシがすでに設定済みのブラウザにPlaywrightやPuppeteerで接続する方法を解説します。
アンチディテクトブラウザの自動化は、難しい問題として語られることがよくあります。実際はそうではありません。ツールが、自動化ライブラリがすでに使い方を知っているもの、つまりデバッグ用エンドポイントを渡してくれさえすればよいのです。
全体像
PowerOpsは、ループバックアドレスにバインドされたHTTP APIを実行します。スクリプトがプロファイルの起動を依頼すると、そのプロファイルのアイデンティティ、ストレージ、プロキシがすでに設定された状態でブラウザが起動し、実行中のブラウザのWebSocketエンドポイントが返されます。スクリプトは、Playwright、Puppeteer、またはDevToolsプロトコルを話せる他のツールで、そのエンドポイントに接続します。
自動化ライブラリは、フィンガープリントが何であるかを知る必要がありません。接続する時点で、環境はすでに整っています。
プロファイルを起動する
curl -s -H "Authorization: Bearer $POWEROPS_API_KEY" \
http://127.0.0.1:50326/status
ステータス確認を除くすべてのルートで、ベアラーキーが必要です。ブラウザを起動すると、接続先のエンドポイントが返されます。
curl -s -X POST \
-H "Authorization: Bearer $POWEROPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"profileId": "..."}' \
http://127.0.0.1:50326/browser/start
ライブラリを接続する
import { chromium } from "playwright";
const res = await fetch("http://127.0.0.1:50326/browser/start", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.POWEROPS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ profileId }),
});
const { ws } = await res.json();
// The profile's identity, storage and proxy are already in place.
const browser = await chromium.connectOverCDP(ws);
const [context] = browser.contexts();
const page = context.pages()[0] ?? await context.newPage();
await page.goto("https://example.com");
重要なのはコメントの行です。スクリプトの中にフィンガープリントの設定はありません。環境を設定するのはスクリプトの仕事ではないからです。それはプロファイルに属し、人が行をクリックして開いても、午前3時にスケジュールされたジョブが開いても、同じままです。
APIがカバーする範囲
- ステータスのルートはヘルスチェックで、キーが不要な唯一のルートです。
- ブラウザのルートは、現在実行中のブラウザを起動、停止、照会します。
- プロファイルのルートは、プロファイルの作成、更新、照会、削除を行います。
- プロキシのルートは、プロキシライブラリを管理し、ルートを割り当てます。
- グループのルートは、プロファイルを一括で整理します。
- 拡張機能のルートは、拡張機能をインポートして割り当てます。
ローカルに保つ仕組み
サインイン済みのアカウントを保持するブラウザを操作するAPIには、その境界について一言触れておく価値があります。
- 127.0.0.1にバインドされます。他のインターフェースで待ち受けさせる設定はありません。
- ステータス確認を除くすべてのルートでベアラーキーが必要です。
- Hostヘッダーを検証し、CORSヘッダーを送らないため、普段使いのブラウザで開いているウェブページからは到達できません。
- シークレットは書き込み専用です。プロファイルのパスワード、二要素認証のシークレット、プロキシのパスワードは、照会ルートから返されることがありません。
真似する価値のあるパターン
グループ内のプロファイルを照会し、一つずつ起動して作業を行い、停止します。すべてを一度に実行するのではなく、数個ずつ実行してください。制約になるのは自分のマシンのメモリで、各プロファイルは本物のブラウザプロセスだからです。
const { profiles } = await api("/profiles?groupId=" + groupId);
for (const batch of chunk(profiles, 5)) {
await Promise.all(batch.map(async (profile) => {
const { ws } = await api("/browser/start", { profileId: profile.id });
try {
await doTheWork(ws);
} finally {
await api("/browser/stop", { profileId: profile.id });
}
}));
}
`finally`でプロファイルを停止することは、見た目以上に重要です。正常に閉じることでブラウザの状態が書き出され、セッションの途中で強制終了されたプロファイルは、確立したばかりのセッションを失うことがあります。
