Seleniumプロキシ認証完全ガイド:ステルス・ローテーション・コンテナ化まで

Seleniumで認証付きレジデンシャルプロキシを設定する全手法を解説。selenium-wire、Firefoxプロファイル、selenium-stealth、IPローテーション、Selenium Gridコンテナ化まで実装コード付き。

Seleniumプロキシ認証完全ガイド:ステルス・ローテーション・コンテナ化まで

なぜSeleniumでプロキシ認証が難しいのか

標準のSelenium WebDriverには、user:pass@host:port形式の認証付きプロキシをネイティブにサポートする機能がありません。ChromeOptions--proxy-serverフラグを設定できますが、これはホストとポートのみを指定でき、ユーザー名とパスワードの受け渡し方法がありません。結果として、ブラウザは認証ポップアップを表示し、Seleniumではそれを処理できずにタイムアウトしてしまいます。

この問題は特にレジデンシャルプロキシを使用する場合に顕著です。データセンタープロキシと異なり、レジデンシャルプロキシはほぼ必ず認証を要求するため、Seleniumユーザーにとって大きな障壁となっています。詳細はWebスクレイピングのユースケースも参照してください。

よく見られる回避策とその問題点:

  • Chrome拡張機能のハック:認証ヘッダーを注入する拡張機能を動的生成して読み込む。動作するが、Chrome Manifest V3で制限が増し、メンテナンス負荷が高い。
  • ローカルプロキシの挟み込み:Squidや3proxyをローカルで起動し、上流の認証プロキシへ転送。動作は安定するが、インフラの複雑さが増す。
  • IPホワイトリスト認証:プロキシプロバイダがIPベースの認証をサポートしていれば可能。ただし、固定IPが必要で柔軟性に欠ける。

本記事では、よりクリーンなアプローチとしてselenium-wireFirefoxプロファイルを使う方法を解説し、さらにステルス対策、IPローテーション、コンテナ化によるスケールまでをカバーします。

Chrome + selenium-wireで認証プロキシを実装

selenium-wireは、SeleniumのThinラッパーとして動作し、リクエストとレスポンスの傍受に加えて認証付きプロキシのネイティブサポートを提供します。標準のWebDriver APIと完全な互換性があるため、既存コードの移行も最小限で済みます。

インストールと基本設定

pip install selenium-wire selenium
from 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=truewindow.chromeの欠落、自動化特有のJSプロパティ)でボット検知されるケースが増えています。Selenium stealth対策は、プロキシ認証と同じくらい重要です。CloudflareやDataDomeなどのWAFは、IPアドレスだけでなくブラウザフィンガープリントも検査しています。

selenium-stealthによる基本対策

pip install selenium-stealth
from 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.webdriverundefinedまたはfalseに設定
  • navigator.pluginsnavigator.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-driverless
import 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: 3

Grid向けのローテーションスクレイパー

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-wirePlaywright
認証プロキシ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のcountrysessionフラグで柔軟なIP管理。
  • Selenium Grid + Dockerで並列スクレイピングをスケール。Grid環境ではプロキシ認証にサイドカーパターンを使用。
  • Playwrightは新規プロジェクトに最適だが、Seleniumのエコシステム成熟度と既存資産の価値は依然として大きい。両者の併用も有効。

ProxyHatのレジデンシャルプロキシは、SeleniumとPlaywrightの両方でシームレスに動作します。geo-targeting、スティッキーセッション、SOCKS5サポートなど、スクレイピングに必要な機能をすべて提供。料金プランから自分のユースケースに合ったプランをお選びください。

始める準備はできましたか?

AIフィルタリングで148か国以上、5,000万以上のレジデンシャルIPにアクセス。

料金を見るレジデンシャルプロキシ
← ブログに戻る