Nodriverプロキシ認証:効果的な3つの修正方法

nodriverプロキシ認証のトップ結果はGitHubのディスカッションであり、ガイドではありません。nodriverにはネイティブなユーザー:パスサポートがありません。ここでは効果的な3つの修正方法と、多くの人が見逃しているワンラインショートカットを紹介します。

nodriverプロキシ認証を検索すると、トップ10の結果はGitHubのディスカッション、デモリポジトリ、別のライブラリに関するStack Overflowのスレッド、そしてRedditの投稿です。実際のガイドはありません。その理由は簡単です:nodriverは非同期CDPであり、undetected-chromedriverの後継です(このプロジェクトは12.8kのGitHubスターと1.3kのフォークがあります)。プロキシにuser:passを渡すネイティブな方法がありません。Chromeはコマンドラインフラグに埋め込まれた資格情報を無視し、nodriverはそれを補完しません。このガイドは、そのディスカッションスレッドがなるべきページです:何が効果的で、何が効果的でないか、そして認証されたプロキシを動作させる3つの修正方法です。

プレーンプロキシは動作しますが、認証プロキシは動作しません

未認証のプロキシはワンライナーです。アドレスをbrowser_argsに渡すと、すべてのリクエストがプロキシIPから出ます:

import nodriver as uc

async def main():
    browser = await uc.start(
        browser_args=["--proxy-server=gate.quantumproxies.io:PORT"],
    )
    page = await browser.get("https://httpbin.org/ip")
    print(await page.get_content())   # shows the proxy exit IP

uc.loop().run_until_complete(main())

今、資格情報を追加します — --proxy-server=http://USER:PASS@host:port — すると壊れます。Chromiumはフラグ形式に資格情報スロットがないためUSER:PASS@セグメントを削除し、プロキシは407 Proxy Authentication Requiredで応答し、ChromeはDOM外に存在するネイティブログインダイアログを表示します。nodriverはそれを見たり埋めたりできません。その407は、407エラー修正ガイドでカバーされているのと同じ壁です:プロキシがあなたを拒否しているのであって、ブラウザではありません。したがって、すべての実際の修正は、別の方法でこのチャレンジに答える必要があります。

修正1:IPホワイトリスト化 — 資格情報なし、ダイアログなし

これはGitHubスレッドが決して言及しないショートカットであり、最も簡単な方法です。スクレイパーが安定したパブリックIPを持つマシンから実行される場合、そのIPをプロバイダーダッシュボードに登録し、資格情報を完全に削除します — ゲートウェイがソースアドレスで認証します。nodriverコードは上記のプレーンな--proxy-serverスニペットのままで、認証コードはまったくありません。すべてのQuantumProxiesプランは、ユーザー:パスと並んでIPホワイトリスト化をサポートしているので、出口IPが固定されている場合はこの方法が推奨されます。その唯一の制限はトポロジーです:それはスクリプトではなくマシンを認証するので、一時的なクラウドランナー、NATの背後にあるコンテナ、IPが変わるCIボックスには次の2つの修正が必要です。

4つのnodriverプロキシ認証ルートの比較:IPホワイトリスト化、CDP Fetch認証ハンドラー、生成されたChrome拡張機能、ローカルリレー
IPが固定されている場合、ホワイトリスト化にはコードが不要です;CDPハンドラーと拡張機能は、そうでない場合に資格情報のチャレンジに答えます。

修正2:CDP Fetchハンドラーでチャレンジに答える

nodriverはChrome DevTools Protocolを直接使用するので、プロセス内で認証チャレンジをインターセプトできます — 拡張ファイルは必要ありません。Fetchドメインをhandle_auth_requests=Trueで有効にし、各AuthRequiredイベントにcontinue_with_authで応答します。2つの詳細、どちらもディスカッション#1798の回答から、動作するかハングするかの違いです:

import asyncio
import nodriver as uc

PROXY = "gate.quantumproxies.io:PORT"   # host:port for --proxy-server
USER, PASS = "USER", "PASS"

class Scraper:
    def __init__(self):
        uc.loop().run_until_complete(self.run())

    async def run(self):
        browser = await uc.start(browser_args=[f"--proxy-server={PROXY}"])
        self.tab = await browser.get("draft:,")        # blank tab first

        # 1) handlers BEFORE enabling the Fetch domain
        self.tab.add_handler(uc.cdp.fetch.RequestPaused, self.on_request)
        self.tab.add_handler(uc.cdp.fetch.AuthRequired, self.on_auth)
        # 2) only now turn on interception with auth handling
        await self.tab.send(uc.cdp.fetch.enable(handle_auth_requests=True))

        page = await browser.get("https://httpbin.org/ip")
        await asyncio.sleep(3)
        print(await page.get_content())

    async def on_auth(self, event):
        # fire-and-forget: awaiting here deadlocks the loop
        asyncio.create_task(self.tab.send(uc.cdp.fetch.continue_with_auth(
            request_id=event.request_id,
            auth_challenge_response=uc.cdp.fetch.AuthChallengeResponse(
                response="ProvideCredentials", username=USER, password=PASS,
            ),
        )))

    async def on_request(self, event):
        asyncio.create_task(self.tab.send(
            uc.cdp.fetch.continue_request(request_id=event.request_id)))

if __name__ == "__main__":
    Scraper()

同じスレッドで浮上した1つの注意点:あるユーザーは、これがプレーンなHTTPページでは動作したがHTTPSでは失敗したことを発見しました。原因は低品質のプロキシであり、コードではありませんでした — より良い出口に切り替えることで解決しました。これはステルススクレイピングの繰り返しの教訓です:ハンドラーはチャレンジに答えますが、IPの評判がサイトがあなたを受け入れるかどうかを決定します。

修正3:生成されたChrome拡張機能

他のコミュニティパターンは、プロキシを設定し、chrome.webRequest.onAuthRequiredを通じて資格情報チャレンジに答える小さなChrome拡張機能を起動時に構築します — SeleniumやPuppeteerで動作するのと同じトリックです。小さなマニフェストとバックグラウンドワーカーを一時ディレクトリに書き込み、--load-extensionでロードします:

import nodriver as uc

async def main():
    # ext_dir holds a Manifest V3 extension: manifest.json + worker.js that
    # calls chrome.proxy.settings.set(...) and returns authCredentials from
    # chrome.webRequest.onAuthRequired. Generate it once, then load it:
    browser = await uc.start(browser_args=[
        "--load-extension=" + ext_dir,
        "--headless=new",   # extensions only load in the NEW headless mode
    ])
    page = await browser.get("https://httpbin.org/ip")
    print(await page.get_content())

uc.loop().run_until_complete(main())

完全なマニフェストとワーカーは、Seleniumプロキシ認証ガイドのManifest V3ファイルと同一です — それらをそのままコピーし、起動呼び出しだけが変わります。2つの注意点がどこでも繰り返されます:拡張機能は--headless=newの下でのみロードされます(プレーンな--headlessは失敗します)、そして未パックのディレクトリはChromeバージョン間でパックされたzipよりも信頼性があります。拡張機能は任意のプロキシタイプを処理するため、CDPルートがあなたと戦っているときのフォールバックとなります。

第4のオプション:ローカルリレー

nodriverにまったく触れたくない場合、資格情報を保持し、127.0.0.1で認証なしのエンドポイントを提示する小さなローカルリレーを実行します。nodriverはループバックアドレスをプレーンフラグで指し、チャレンジを全く見ません。これはSOCKS5に最もクリーンなルートです。Chromiumは認証されたプロキシを完全に拒否します(Chromiumバグ40829748として追跡されています)。最小限のリレー、既製のツール、そしてそれが過剰である場合については、プロキシリレーガイドでカバーしています。

SOCKS5に関する注意

未認証のSOCKS5はフラグを通じて動作します — --proxy-server=socks5://host:port — しかし、認証されたSOCKS5は動作せず、CDPハンドラーは助けになりません。ChromiumはSOCKS5のユーザー名/パスワードサポートを一度も提供していません。実用的な答えは同じ3つです:IPをホワイトリスト化し、リレーを実行し、またはプロバイダーのHTTPエンドポイントを使用します。すべてのQuantumProxiesプランは、同じゲートウェイでHTTPとSOCKS5プロキシの両方を公開しているので、HTTPエンドポイントに切り替えることが最も速いSOCKS5修正となることがよくあります。アンチディテクトブラウザのファミリーとして、認証プロキシマップはnodriver、zendriver、その他を並べて比較しています。

プロキシゲートウェイでのIPホワイトリスト化がnodriverスクリプトをプレーンプロキシフラグで実行し、資格情報ダイアログなしで動作させる流れ
出口IPが安定している場合、ホワイトリスト化はマシンを認証し、認証コードは完全に消えます。

よくある質問

nodriverは認証されたプロキシをサポートしていますか?

ネイティブにはサポートしていません。未認証のプロキシをbrowser_args=["--proxy-server=host:port"]で渡すことはできますが、Chromiumによってそのフラグ内のuser:passは削除されます。認証するには、プロバイダーでIPをホワイトリスト化するか、CDP Fetch.AuthRequiredハンドラーでチャレンジに答えるか、プロキシ認証Chrome拡張機能を生成するか、資格情報を保持するローカルリレーを実行します。

なぜ私のnodriver認証ハンドラーはイベントを受信しないのですか?

ほとんどの場合、ハンドラーを登録する前にFetchドメインを有効にしたためです。nodriverの内部enableは登録を上書きするので、イベントはコールバックに届きません。RequestPausedAuthRequiredハンドラーを最初に追加し、次にfetch.enable(handle_auth_requests=True)を呼び出します。また、応答をasyncio.create_taskでラップし、待機がループをブロックしないようにします。

nodriverはユーザー名とパスワードでSOCKS5プロキシを使用できますか?

いいえ。Chromiumは認証されたSOCKS5をサポートしておらず(Chromiumバグ40829748)、nodriverはその制限を引き継いでいます。未認証のSOCKS5は--proxy-server=socks5://host:portで動作します。認証されたSOCKS5の場合、IPをホワイトリスト化するか、資格情報を追加するローカルリレーを実行するか、基本認証をクリーンに処理するプロバイダーのHTTPエンドポイントに切り替えます。

認証されたプロキシにはnodriverかzendriverか?

どちらも同じギャップと同じ修正方法を共有しています。zendriverはnodriverのコミュニティフォークです。zendriverは認証の問題が公然と議論されているより活発な問題トラッカーを持っていますが、動作する方法は同一です。フォークを使用している場合、フォーク固有のセットアップはここでのすべてを反映しています — CDPハンドラーとホワイトリスト化は同じように動作します。

正直な要約:nodriverはプロキシ認証を自動で行いませんが、地図を知っていれば問題ありません。IPが安定している場合はホワイトリスト化し、そうでない場合はCDPハンドラーまたは拡張機能を使用し、SOCKS5のためにリレーをバックポケットに入れておきます。上記のコードはチャレンジに答えますが、クリーンな住宅用出口が実際にドアを通過させます。

ホワイトリスト化された住宅用IPでnodriverを実行する