ブラウザ自動化の開発者なら誰もが必ず一度はハマる罠があります。Chrome の起動フラグに --proxy-server=http://user:pass@host:port を追加すると、ブラウザ自体は問題なく起動するのに、どのページも、スクリプトからは見ることもクリックすることもできない認証ポップアップで固まってしまうのです。Chrome はプロキシフラグから認証情報を黙って削ぎ落とします。エラーもログ行もなく、ただ誰も見ていないブラウザの中にモーダルダイアログが出るだけです。
本稿は、2026 年に Selenium と Playwright で実際に動作するプロキシ認証手法の完全な見取り図です — どれを使うべきか、どれを避けるべきか、そしてそれぞれの正確なコードを示します。(これらのツールでのプロキシ選択とローテーション戦略については、Playwright & Puppeteer でプロキシを使うを参照してください — 本稿は純粋に「認証情報を通す」ことだけを扱います。)
コマンドライン HTTP クライアントは、プロキシ URL から認証情報をパースし、プロキシの 407 Proxy Authentication Required に対して Proxy-Authorization ヘッダーで自動的に応答します。Chromium はそうしません。--proxy-server は scheme://host:port のみを受け付け、407 が返ってくるとインタラクティブなダイアログを表示します。ヘッドレスモードではダイアログすら出ず — リクエストはただ失敗します。
つまり問題は決して「どうやってパスワードを URL に入れるか」ではなく —「どの層が代わりに 407 に応答するか」です。動作する答えは 4 つ、良いものから順に紹介します。
Playwright はこの問題を正しく解決しました。CDP 経由でプロキシ認証チャレンジに自前で応答します。認証情報は launch または context のオプションに渡します:
# Python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={
"server": "http://us.jibaoproxy.com:913",
"username": "USERNAME-session-job1",
"password": "PASSWORD",
}
)
// Node.js - and per-context, which is where it gets powerful:
const browser = await chromium.launch({
proxy: { server: 'per-context' } // declare intent at launch
});
const ctx = await browser.newContext({
proxy: {
server: 'http://us.jibaoproxy.com:913',
username: 'USERNAME-session-job2',
password: 'PASSWORD',
}
});
per-context プロキシは、1 つのブラウザプロセスで N 個の context を N 個の異なる sticky セッション上で動かせることを意味します — 本格的なあらゆる Playwright スクレイピング構成の基盤です。Playwright を使っているなら、これで完了です。検証セクションまで読み飛ばしてください。以下のすべては、Selenium に同等の機能がないために存在します。
selenium-wire は、上流の認証を処理するローカル MITM プロキシで Selenium をラップします:
from seleniumwire import webdriver # pip install selenium-wire
options = {
"proxy": {
"http": "http://USERNAME:[email protected]:913",
"https": "https://USERNAME:[email protected]:913",
}
}
driver = webdriver.Chrome(seleniumwire_options=options)
driver.get("https://example.com")
本番投入前に知っておくべき注意点:
古典的なアプローチ:プロキシを設定し、chrome.webRequest.onAuthRequired 経由で認証に応答する小さな生成済み拡張機能です。MITM も追加プロセスもなく、素の Selenium で動作します:
import zipfile, json
HOST, PORT = "us.jibaoproxy.com", 913
USER, PASS = "USERNAME-session-sel1", "PASSWORD"
manifest = {
"version": "1.0.0", "manifest_version": 2, "name": "Proxy Auth",
"permissions": ["proxy", "webRequest", "webRequestBlocking", ""],
"background": {"scripts": ["background.js"]},
}
background = f"""
chrome.proxy.settings.set({{value: {{mode: "fixed_servers", rules: {{
singleProxy: {{scheme: "http", host: "{HOST}", port: {PORT}}}
}}}}, scope: "regular"}}, () => {{}});
chrome.webRequest.onAuthRequired.addListener(
() => ({{authCredentials: {{username: "{USER}", password: "{PASS}"}}}}),
{{urls: [""]}}, ["blocking"]
);
"""
with zipfile.ZipFile("proxy_auth.zip", "w") as zp:
zp.writestr("manifest.json", json.dumps(manifest))
zp.writestr("background.js", background)
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_extension("proxy_auth.zip")
driver = webdriver.Chrome(options=opts)
落とし穴が 2 つ:拡張機能は従来のヘッドレスモードでは読み込まれません — --headless=new(Chrome 109+)を使うか、ヘッド付きで実行してください。そして Manifest V2 のバックグラウンドスクリプトはサイドロードする自動化拡張機能では今も動作しますが、Chrome の MV2 廃止スケジュールには注意してください — MV3 版では service worker と同じ onAuthRequired リスナーが必要になります。
認証情報を保持する小さなローカルプロキシを動かし、ブラウザは認証なしで localhost に向けます。あらゆるブラウザ、あらゆるドライバー、あらゆる言語で、ヘッドレスかどうかを問わず動作します:
# Using pproxy (pip install pproxy) - terminal 1:
pproxy -l http://127.0.0.1:8899 \
-r "http://USERNAME:[email protected]:913"
# Your script - terminal 2: plain Selenium, no auth needed
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://127.0.0.1:8899")
driver = webdriver.Chrome(options=opts)
これは Firefox/geckodriver(拡張機能のトリックが使えない)や、Electron アプリのテストのような特殊な環境にとっても最もクリーンな答えです。コスト:監督すべき可動部分がもう 1 つ増えること、そして per-context ルーティングにはセッションごとに 1 つのフォワーダーポートが必要になることです。
| 手法 | 動作環境 | ヘッドレス | per-context セッション | 本番での評価 |
|---|---|---|---|---|
| Playwright ネイティブ | Playwright | 可 | 可 | デフォルトの選択肢 |
| selenium-wire | Selenium (Py) | 可 | ドライバー単位 | 中規模なら問題なし |
| 認証拡張機能 | Selenium(任意の言語) | --headless=new のみ | ドライバー単位 | 堅実、MV3 に注意 |
| ローカルフォワーダー | すべて | 可 | セッションごとに 1 ポート | 万能のフォールバック |
本物の IP へのサイレントなフォールバックこそ、アカウントを BAN させる失敗モードです。すべての実行の冒頭で出口 IP をアサートしてください:
ip = driver.execute_script(
"return fetch('https://api.ipify.org').then(r => r.text())"
) if hasattr(driver, 'execute_script') else page.evaluate(
"fetch('https://api.ipify.org').then(r => r.text())"
)
assert ip != MY_REAL_IP, "Proxy not applied - aborting before we leak"
--proxy-server に渡しています。Chrome がそれを削除しました。4 つの手法のいずれかを使ってください。ERR_PROXY_CONNECTION_FAILED — ホスト/ポートが誤っているか、プロトコルが不一致です。まず同じ行を curl -x でテストしてください。curl で動くなら、問題はブラウザ側の配線です。--headless=new に切り替えてください。ブラウザは user:pass@ 形式のプロキシ URL を読みません — 何かが代わりに 407 に応答しなければなりません。Playwright ではその「何か」が組み込まれています。Selenium では selenium-wire、生成した認証拡張機能、またはローカルフォワーダーです。比較表からあなたのスタックに合わせて選び、そして すべての実行で出口 IP をアサートしてください。そうすればサイレントな認証失敗がクロールの途中で本物のアドレスを漏らすことは決してありません。
新規ユーザーは登録時に500MBを獲得、さらに初回チャージにボーナスが付きます。期間限定オファーです。