なぜSeleniumでプロキシ認証が難しいのか
標準のSelenium WebDriverには、user:pass@host:port形式の認証付きプロキシをネイティブにサポートする機能がありません。ChromeOptionsで--proxy-serverフラグを設定できますが、これはホストとポートのみを指定でき、ユーザー名とパスワードの受け渡し方法がありません。結果として、ブラウザは認証ポップアップを表示し、Seleniumではそれを処理できずにタイムアウトしてしまいます。
この問題は特にレジデンシャルプロキシを使用する場合に顕著です。データセンタープロキシと異なり、レジデンシャルプロキシはほぼ必ず認証を要求するため、Seleniumユーザーにとって大きな障壁となっています。詳細はWebスクレイピングのユースケースも参照してください。
よく見られる回避策とその問題点:
- Chrome拡張機能のハック:認証ヘッダーを注入する拡張機能を動的生成して読み込む。動作するが、Chrome Manifest V3で制限が増し、メンテナンス負荷が高い。
- ローカルプロキシの挟み込み:Squidや3proxyをローカルで起動し、上流の認証プロキシへ転送。動作は安定するが、インフラの複雑さが増す。
- IPホワイトリスト認証:プロキシプロバイダがIPベースの認証をサポートしていれば可能。ただし、固定IPが必要で柔軟性に欠ける。
本記事では、よりクリーンなアプローチとしてselenium-wireとFirefoxプロファイルを使う方法を解説し、さらにステルス対策、IPローテーション、コンテナ化によるスケールまでをカバーします。
Chrome + selenium-wireで認証プロキシを実装
selenium-wireは、SeleniumのThinラッパーとして動作し、リクエストとレスポンスの傍受に加えて認証付きプロキシのネイティブサポートを提供します。標準のWebDriver APIと完全な互換性があるため、既存コードの移行も最小限で済みます。
インストールと基本設定
pip install selenium-wire seleniumfrom seleniumwire import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new")
options.add_argument("--disable-gpu")
# ProxyHatの認証付きレジデンシャルプロキシを設定
proxy_config = {
"proxy": {
"http": "http://user-country-US:PASSWORD@gate.proxyhat.com:8080",
"https": "http://user-country-US:PASSWORD@gate.proxyhat.com:8080",
"no_proxy": "localhost,127.0.0.1"
}
}
driver = webdriver.Chrome(
options=options,
seleniumwire_options=proxy_config
)
driver.get("https://httpbin.org/ip")
print(driver.page_source)
driver.quit()selenium-wireは裏でローカルミドルウェアプロキシを起動し、ブラウザからのトラフィックを傍受して上流プロキシへ認証ヘッダー付きで転送します。Chrome拡張機能のハックが不要で、Manifest V3の制限も受けません。これはSelenium proxy authの最もシンプルな解決策です。
リクエスト傍受によるデバッグとCAPTCHA検出
selenium-wireの追加メリットとして、全リクエストとレスポンスにアクセスできます。これにより、プロキシ経由のトラフィックをデバッグしたり、CAPTCHAチャレンジの検出に活用できます。
# 全リクエストを確認
for request in driver.requests:
if request.response:
print(f"{request.method} {request.url} → {request.response.status_code}")
# Cloudflareチャレンジの検出
for request in driver.requests:
if request.response and "cf-ray" in request.response.headers:
print(f"Cloudflare検出: {request.url}")このネットワーク傍受機能は、スクレイピングの成功率向上に直結します。CAPTCHAがトリガーされたことをリアルタイムで検知できれば、IPのローテーションやリトライ戦略を動的に調整できます。
Firefoxでプロキシプロファイルを動的生成
Chrome + selenium-wireが最もシンプルですが、Firefoxを使用する場合はプロファイルベースの設定が可能です。Firefoxはabout:config経由でプロキシ認証情報を直接設定できます。
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
import tempfile, os, json
def create_firefox_proxy_profile(proxy_host, proxy_port, proxy_user, proxy_pass):
"""認証付きプロキシ用のFirefoxプロファイルを動的生成"""
profile_dir = tempfile.mkdtemp(prefix="firefox_proxy_")
# プロキシ設定をuser.jsに書き込み
user_js = os.path.join(profile_dir, "user.js")
with open(user_js, "w") as f:
f.write(f"""
user_pref("network.proxy.type", 1);
user_pref("network.proxy.http", "{proxy_host}");
user_pref("network.proxy.http_port", {proxy_port});
user_pref("network.proxy.ssl", "{proxy_host}");
user_pref("network.proxy.ssl_port", {proxy_port});
user_pref("network.proxy.no_proxies_on", "localhost, 127.0.0.1");
""")
# 認証情報をlogins.jsonに保存
logins_json = os.path.join(profile_dir, "logins.json")
with open(logins_json, "w") as f:
json.dump({
"logins": [{
"hostname": f"http://{proxy_host}:{proxy_port}",
"username": proxy_user,
"password": proxy_pass
}]
}, f)
return profile_dir
# 使用例:ドイツ・ベルリンのIPでスクレイピング
profile_dir = create_firefox_proxy_profile(
proxy_host="gate.proxyhat.com",
proxy_port=8080,
proxy_user="user-country-DE-city-berlin",
proxy_pass="PASSWORD"
)
options = Options()
options.add_argument("--headless")
options.add_argument("-profile")
options.add_argument(profile_dir)
driver = webdriver.Firefox(options=options)
driver.get("https://httpbin.org/ip")
print(driver.page_source)
driver.quit()
# クリーンアップ
import shutil
shutil.rmtree(profile_dir, ignore_errors=True)注意:Firefoxのプロファイルベースの認証は、一部のプロキシタイプで認証ダイアログが表示される場合があります。その場合は、selenium-wireのFirefox対応版(実験的)またはローカルフォワーディングプロキシの使用を検討してください。また、本番環境ではlogins.jsonの平文保存にセキュリティ上の懸念があるため、nsILoginManagerの使用を推奨します。
selenium-stealth / selenium-driverlessで指紋を隠す
プロキシでIPを変えても、WebDriverの指紋(navigator.webdriver=true、window.chromeの欠落、自動化特有のJSプロパティ)でボット検知されるケースが増えています。Selenium stealth対策は、プロキシ認証と同じくらい重要です。CloudflareやDataDomeなどのWAFは、IPアドレスだけでなくブラウザフィンガープリントも検査しています。
selenium-stealthによる基本対策
pip install selenium-stealthfrom selenium import webdriver
from selenium_stealth import stealth
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=options)
# ステルス設定を適用
stealth(driver,
languages=["ja-JP", "en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
driver.get("https://bot.sannysoft.com/")
# ステルステストページで検出項目を確認
input("結果を確認したらEnterを押してください...")
driver.quit()selenium-stealthは以下の指紋を修正します:
navigator.webdriverをundefinedまたはfalseに設定navigator.pluginsとnavigator.mimeTypesを自然な値で偽装navigator.languagesを実際のブラウザと一致する値に設定- WebGLのベンダー/レンダラー情報を偽装
- Chromeオブジェクトの検出を回避(
window.chromeの復元)
selenium-driverless:より高度なステルス
selenium-driverlessは、Chrome DevTools Protocol(CDP)を直接使用し、Chromeの自動化検出フラグをパッチレベルで無効化します。Cloudflare TurnstileやDataDomeなどの高度なWAFに対して、selenium-stealthより高い回避率を示します。
pip install selenium-driverlessimport asyncio
from selenium_driverless.webdriver import Chrome
async def stealth_scrape():
async with Chrome() as driver:
# CDP経由でプロキシ認証を設定
await driver.set_proxy(
host="gate.proxyhat.com",
port=8080,
username="user-country-US",
password="PASSWORD"
)
await driver.get("https://nowsecure.nl/")
content = await driver.page_source
print("Cloudflare通過" if "cf-chl-bypass" not in content else "検出されました")
asyncio.run(stealth_scrape())selenium-driverlessの利点:標準的なChromeバイナリを使用しつつ、自動化フラグをパッチで無効化するため、navigator.webdriverの検出だけでなく、より高度なCDPベースの検出も回避できます。ただし、非同期APIのみのサポートで、既存の同期コードからの移行には工数がかかります。
実践的アプローチ:selenium-stealthは軽量で既存コードへの統合が容易。selenium-driverlessは強力だが非同期移行が必要。まずはselenium-stealthで基本指紋を隠し、Cloudflare等で検出される場合にselenium-driverlessへの移行を検討する段階的アプローチを推奨します。
ローテーションプロキシプールパターン
単一IPでの連続スクレイピングは、レート制限やIPバンを引き起こします。ローテーションプロキシプールパターンでは、各WebDriverセッションに新しいIPを割り当て、セッション終了後にIPを解放します。これはSERPトラッキングや価格モニタリングで特に重要です。
セッション単位のIPローテーション実装
import itertools
from seleniumwire import webdriver
from selenium.webdriver.chrome.options import Options
class ProxyPool:
"""ローテーションプロキシプールマネージャー"""
def __init__(self, username, password, countries=None):
self.username = username
self.password = password
self.countries = countries or ["US", "DE", "GB", "JP", "FR"]
self._cycle = itertools.cycle(self.countries)
def next_proxy_config(self):
"""次の国のプロキシ設定を返す(リクエストごとのローテーション)"""
country = next(self._cycle)
proxy_url = (
f"http://{self.username}-country-{country}"
f":{self.password}@gate.proxyhat.com:8080"
)
return {
"proxy": {
"http": proxy_url,
"https": proxy_url,
}
}
def sticky_proxy_config(self, session_id, country="US"):
"""スティッキーセッション用のプロキシ設定(同じIPを維持)"""
proxy_url = (
f"http://{self.username}-country-{country}"
f"-session-{session_id}:{self.password}@gate.proxyhat.com:8080"
)
return {
"proxy": {
"http": proxy_url,
"https": proxy_url,
}
}
def create_driver(proxy_config, headless=True):
"""プロキシ設定付きWebDriverを作成"""
options = Options()
if headless:
options.add_argument("--headless=new")
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(
options=options,
seleniumwire_options=proxy_config
)
return driver
# 使用例:5ページを異なるIPでスクレイピング
pool = ProxyPool(username="myuser", password="mypass")
urls = [
"https://httpbin.org/ip",
"https://httpbin.org/ip",
"https://httpbin.org/ip",
"https://httpbin.org/ip",
"https://httpbin.org/ip",
]
for url in urls:
config = pool.next_proxy_config()
driver = create_driver(config)
try:
driver.get(url)
ip = driver.find_element("tag name", "body").text.strip()
print(f"IP: {ip}")
finally:
driver.quit()スティッキーセッション vs リクエストごとのローテーション
| 戦略 | ユースケース | メリット | デメリット |
|---|---|---|---|
| リクエストごと | SERP収集、価格チェック | IPバンのリスク最小 | ログイン状態を維持できない |
| スティッキーセッション | ECサイトのカート操作、SNSスクレイピング | セッション一セッション一貫性を維持 | 同一IPでの連続アクセス |
| ハイブリッド | 大規模クローラー | 柔軟性が高い | 実装の複雑さが増す |
ProxyHatのスティッキーセッション機能では、session-{id}フラグをユーザー名に含めることで、同じセッションIDの間は同一IPが割り当てられます。セッションIDを変更すると新しいIPが割り当てられるため、プログラマティックなIP切り替えが可能です。対応国の一覧はプロキシロケーションページを参照してください。
Selenium Gridとコンテナ化でスケール
単一マシンでの並列スクレイピングには限界があります。Selenium Gridを使用すると、複数のノードでWebDriverセッションを分散実行でき、コンテナ化により環境の再現性とスケール性を確保できます。
Docker ComposeでSelenium Gridを構築
# docker-compose.yml
version: "3.8"
services:
selenium-hub:
image: selenium/hub:4.18.0
ports:
- "4442:4442"
- "4443:4443"
- "4444:4444"
environment:
- SE_SESSION_REQUEST_TIMEOUT=300
- SE_NODE_SESSION_TIMEOUT=300
chrome-node:
image: selenium/node-chrome:4.18.0
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
- SE_NODE_MAX_SESSIONS=4
deploy:
replicas: 3Grid向けのローテーションスクレイパー
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from concurrent.futures import ThreadPoolExecutor, as_completed
import uuid
GRID_URL = "http://localhost:4444/wd/hub"
def scrape_with_rotation(target_url, country="US"):
"""Grid上でプロキシ付きセッションを実行"""
session_id = uuid.uuid4().hex[:8]
options = Options()
options.add_argument("--headless=new")
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("--proxy-server=http://gate.proxyhat.com:8080")
# 注: Grid環境ではselenium-wireが使えないため、
# IPホワイトリスト認証またはサイドカープロキシを使用
driver = webdriver.Remote(
command_executor=GRID_URL,
options=options
)
try:
driver.get(target_url)
return {
"session": session_id,
"country": country,
"title": driver.title,
"status": "success"
}
except Exception as e:
return {"session": session_id, "status": f"error: {e}"}
finally:
driver.quit()
# 12タスクを並列実行(6ワーカー)
targets = [
("https://httpbin.org/ip", "US"),
("https://httpbin.org/ip", "DE"),
("https://httpbin.org/ip", "GB"),
("https://httpbin.org/ip", "JP"),
("https://httpbin.org/ip", "FR"),
("https://httpbin.org/ip", "US"),
("https://httpbin.org/ip", "DE"),
("https://httpbin.org/ip", "GB"),
("https://httpbin.org/ip", "JP"),
("https://httpbin.org/ip", "FR"),
("https://httpbin.org/ip", "US"),
("https://httpbin.org/ip", "DE"),
]
with ThreadPoolExecutor(max_workers=6) as executor:
futures = [
executor.submit(scrape_with_rotation, url, country)
for url, country in targets
]
for future in as_completed(futures):
result = future.result()
print(result)Grid環境でのプロキシ認証の課題:Selenium GridのRemote WebDriverではselenium-wireが使用できません(ローカルミドルウェアプロキシがGridノード上で動作しないため)。対応策として:
- IPホワイトリスト認証:ProxyHatがサポートするIPベースの認証を利用。Gridノードの固定IPをホワイトリストに登録。
- Squid/3proxyコンテナのサイドカー配置:各ノードコンテナにローカルフォワーディングプロキシをサイドカーとして配置し、認証をローカルプロキシで処理。
- Chrome拡張機能のハック:Grid環境でも動作するが、Manifest V3の制限に注意。一時的な解決策として有用。
サイドカープロキシのDocker Compose例
# プロキシサイドカー付きノード構成の例
chrome-node-with-proxy:
image: selenium/node-chrome:4.18.0
depends_on:
- selenium-hub
- local-proxy
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
- SE_NODE_MAX_SESSIONS=4
- http_proxy=http://local-proxy:3128
- https_proxy=http://local-proxy:3128
local-proxy:
image: ubuntu/squid:latest
volumes:
- ./squid.conf:/etc/squid/squid.conf
# squid.confで上流プロキシ(gate.proxyhat.com:8080)への
# 認証付きフォワーディングを設定Playwright vs Selenium:どちらを選ぶべきか
2024年以降、PlaywrightはSeleniumの代替として急速に普及しています。プロキシ認証とステルスの観点から、両者を比較します。
| 機能 | Selenium + selenium-wire | Playwright |
|---|---|---|
| 認証プロキシ | selenium-wireで対応(追加依存) | ネイティブサポート |
| ステルス対策 | selenium-stealth / driverlessで対応 | 組み込みステルス機能(一部) |
| プロキシローテーション | 手動実装必要 | コンテキスト単位で容易 |
| ブラウザコンテキスト | なし(1セッション=1ブラウザ) | あり(1ブラウザで複数コンテキスト) |
| 自動待機 | 明示的/暗黙的Wait | 自動待機(Auto-wait) |
| ネットワーク傍受 | selenium-wireで対応 | ネイティブ(route API) |
| エコシステム成熟度 | 非常に高い(20年+) | 成長中(5年+) |
| 既存テスト資産 | 膨大(Selenium IDE、各種FW) | 移行必要 |
| Grid / 並列実行 | Selenium Grid(成熟) | 組み込み並列 + 外部Grid |
Playwrightを選ぶべきケース
- 新規プロジェクト:既存のSelenium資産がなく、プロキシ認証とステルスが必須ならPlaywrightが最適。認証プロキシが1行で設定できる。
- ブラウザコンテキストの活用:1つのブラウザインスタンスで複数の独立したセッション(異なるプロキシ、Cookie、ストレージ)を管理したい場合。リソース消費を大幅に削減できる。
- ネットワーク傍受とモック:リクエストのモック、変更、ブロックをコード内で行いたい場合。selenium-wireより洗練されたAPI。
Seleniumを選ぶべきケース
- 既存の大規模テストスイート:Seleniumベースのテスト資産が既にあり、移行コストが高い場合。段階的にselenium-wireとselenium-stealthを導入する方が現実的。
- エンタープライズツールの統合:Sauce Labs、BrowserStack、LambdaTestなどのクラウドテストプラットフォームとの統合が必要な場合。これらのプラットフォームはSeleniumプロトコルを中心に構築されている。
- 多言語バインディング:Java、C#、Ruby、PHPなど、Playwrightがサポートしない言語での開発が必要な場合。
実用的なアプローチ:既存のSeleniumプロジェクトにはselenium-wire + selenium-stealthを段階的に導入し、新規のスクレイピングプロジェクトではPlaywrightを検討する。両方を並行運用することも、マイクロサービスアーキテクチャでは一般的です。
Key Takeaways
- Seleniumのプロキシ認証問題はselenium-wireで解決。Chrome拡張機能のハックは避ける。
- Firefoxはプロファイルベースのプロキシ設定が可能だが、認証ダイアログの問題に注意。selenium-wireの使用を優先。
- selenium-stealthで基本指紋を隠し、高度なWAF対策にはselenium-driverlessを検討。段階的導入が現実的。
- ローテーションプロキシプールパターンでIPバンを回避。ProxyHatの
countryとsessionフラグで柔軟なIP管理。 - Selenium Grid + Dockerで並列スクレイピングをスケール。Grid環境ではプロキシ認証にサイドカーパターンを使用。
- Playwrightは新規プロジェクトに最適だが、Seleniumのエコシステム成熟度と既存資産の価値は依然として大きい。両者の併用も有効。
ProxyHatのレジデンシャルプロキシは、SeleniumとPlaywrightの両方でシームレスに動作します。geo-targeting、スティッキーセッション、SOCKS5サポートなど、スクレイピングに必要な機能をすべて提供。料金プランから自分のユースケースに合ったプランをお選びください。






