Authentification Proxy Nodriver : Les Trois Solutions Qui Fonctionnent

Le résultat principal pour l'authentification proxy nodriver est une discussion sur GitHub, pas un guide. nodriver n'a pas de support natif pour user:pass — voici les trois solutions qui fonctionnent, et le raccourci d'une ligne que la plupart des gens manquent.

Recherchez authentification proxy nodriver et les dix premiers résultats sont une discussion GitHub, un dépôt démo, quelques fils Stack Overflow concernant une autre bibliothèque, et un post Reddit — pas de guide réel. La raison est simple : nodriver, le successeur asynchrone CDP de undetected-chromedriver (ce projet a 12,8k étoiles GitHub et 1,3k forks), n'a pas de moyen natif pour passer user:pass à un proxy. Chrome ignore les identifiants intégrés dans un flag de ligne de commande, et nodriver ne le compense pas. Ce guide est la page que ce fil de discussion aurait dû devenir : ce qui fonctionne, ce qui ne fonctionne pas, et les trois solutions qui permettent de faire fonctionner un proxy authentifié.

Un proxy simple fonctionne ; un proxy authentifié ne fonctionne pas

Un proxy non authentifié est une ligne. Passez l'adresse via browser_args et chaque requête sort de l'IP du proxy :

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())

Ajoutez maintenant des identifiants — --proxy-server=http://USER:PASS@host:port — et cela casse. Chromium supprime le segment USER:PASS@ car le format du flag n'a pas de slot pour les identifiants, puis le proxy répond avec 407 Proxy Authentication Required et Chrome affiche une boîte de dialogue de connexion native qui vit en dehors du DOM. nodriver ne peut ni la voir ni la remplir. Ce 407 est le même mur couvert dans notre guide pour résoudre les erreurs 407 : le proxy vous rejette, pas le navigateur. Donc chaque solution réelle doit répondre au défi d'une autre manière.

Solution 1 : Whitelisting IP — pas d'identifiants, pas de dialogue

C'est le raccourci que les fils GitHub ne mentionnent jamais, et c'est de loin le plus simple. Si votre scraper fonctionne depuis une machine avec une IP publique stable, enregistrez cette IP dans le tableau de bord de votre fournisseur et abandonnez complètement les identifiants — la passerelle vous authentifie par adresse source. Le code nodriver reste le simple extrait --proxy-server ci-dessus, sans code d'authentification du tout. Chaque plan QuantumProxies prend en charge le whitelisting IP en plus de user:pass sur ses proxies résidentiels, donc c'est la voie recommandée chaque fois que votre IP de sortie est fixe. Sa seule limite est topologique : elle authentifie une machine, pas un script, donc les runners cloud éphémères, les conteneurs derrière NAT, et les boîtes CI avec des IP changeantes ont besoin de l'une des deux solutions suivantes.

Comparaison de quatre routes d'authentification proxy nodriver : whitelisting IP, un gestionnaire d'authentification CDP Fetch, une extension Chrome générée, et un relais local
Le whitelisting ne coûte aucun code lorsque votre IP est fixe ; le gestionnaire CDP et l'extension répondent au défi d'authentification lorsqu'elle ne l'est pas.

Solution 2 : répondre au défi avec un gestionnaire CDP Fetch

nodriver parle directement le Chrome DevTools Protocol, donc vous pouvez intercepter le défi d'authentification en cours de processus — pas besoin de fichier d'extension. Activez le domaine Fetch avec handle_auth_requests=True, puis répondez à chaque événement AuthRequired avec continue_with_auth. Deux détails, tous deux issus de la réponse à la discussion #1798, font la différence entre fonctionner et bloquer :

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()

Une mise en garde est apparue dans le même fil : un utilisateur a constaté que cela fonctionnait sur des pages HTTP simples mais échouait sur HTTPS, et le coupable était un proxy de mauvaise qualité, pas le code — passer à une meilleure sortie a résolu le problème. C'est la leçon récurrente du scraping furtif : le gestionnaire répond au défi, mais la réputation IP décide si le site vous laisse entrer.

Solution 3 : une extension Chrome générée

L'autre modèle communautaire construit une petite extension Chrome au démarrage qui définit à la fois le proxy et répond au défi d'authentification via chrome.webRequest.onAuthRequired — le même truc qui fonctionne dans Selenium et Puppeteer. Vous écrivez un petit manifeste plus un travailleur en arrière-plan dans un répertoire temporaire et le chargez via --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())

Le manifeste complet et le travailleur sont identiques aux fichiers Manifest V3 dans notre guide d'authentification proxy Selenium — copiez-les tels quels, seule l'appel de lancement change. Deux pièges se répètent partout : les extensions ne se chargent que sous --headless=new (plain --headless échoue), et un répertoire non emballé est plus fiable à travers les versions de Chrome qu'un zip emballé. L'extension gère tout type de proxy, ce qui en fait le recours lorsque la route CDP vous combat.

La quatrième option : un relais local

Si vous préférez ne pas toucher à nodriver du tout, exécutez un petit relais local qui détient les identifiants et présente un point de terminaison sans authentification sur 127.0.0.1. nodriver pointe alors à l'adresse de boucle avec le flag simple et ne voit jamais un défi. C'est la route la plus propre pour SOCKS5, où Chromium refuse catégoriquement les proxies authentifiés (suivi comme bug Chromium 40829748). Nous couvrons le relais minimal, les outils prêts à l'emploi, et quand c'est exagéré dans le guide du relais proxy.

Une note sur SOCKS5

SOCKS5 non authentifié fonctionne via le flag — --proxy-server=socks5://host:port — mais SOCKS5 authentifié ne fonctionne pas, et aucun gestionnaire CDP ne vous sauvera car Chromium n'a jamais livré de support pour le nom d'utilisateur/mot de passe SOCKS5. Les réponses pratiques sont les mêmes trois : whitelister l'IP, exécuter un relais, ou utiliser le point de terminaison HTTP du fournisseur à la place. Chaque plan QuantumProxies expose à la fois des proxies HTTP et SOCKS5 sur la même passerelle, donc passer au point de terminaison HTTP est souvent la solution SOCKS5 la plus rapide de toutes. Pour les navigateurs anti-détection en tant que famille, la carte des proxies authentifiés compare nodriver, zendriver et le reste côte à côte.

Flux montrant comment le whitelisting IP à la passerelle proxy permet à un script nodriver de fonctionner avec un flag proxy simple et sans dialogue d'identification
Lorsque l'IP de sortie est stable, le whitelisting authentifie la machine et le code d'authentification disparaît entièrement.

Questions fréquemment posées

nodriver prend-il en charge les proxies authentifiés ?

Pas nativement. Vous pouvez passer un proxy non authentifié via browser_args=["--proxy-server=host:port"], mais user:pass dans ce flag est supprimé par Chromium. Pour authentifier, vous devez soit whitelister votre IP avec le fournisseur, répondre au défi avec un gestionnaire CDP Fetch.AuthRequired, générer une extension Chrome d'authentification proxy, ou exécuter un relais local qui détient les identifiants.

Pourquoi mon gestionnaire d'auth nodriver ne reçoit-il aucun événement ?

Presque toujours parce que vous avez activé le domaine Fetch avant d'enregistrer les gestionnaires. L'appel interne enable de nodriver remplace l'enregistrement, donc les événements n'atteignent jamais votre callback. Ajoutez d'abord les gestionnaires RequestPaused et AuthRequired, puis appelez fetch.enable(handle_auth_requests=True). Enveloppez également vos réponses dans asyncio.create_task pour qu'attendre ne puisse pas bloquer la boucle.

nodriver peut-il utiliser un proxy SOCKS5 avec un nom d'utilisateur et un mot de passe ?

Non. Chromium ne prend pas en charge SOCKS5 authentifié (bug Chromium 40829748), et nodriver hérite de cette limite. SOCKS5 non authentifié fonctionne via --proxy-server=socks5://host:port. Pour SOCKS5 authentifié, whitelistez votre IP, exécutez un relais local qui ajoute les identifiants, ou passez au point de terminaison HTTP du fournisseur, qui gère l'authentification Basic proprement.

nodriver ou zendriver pour les proxies authentifiés ?

Les deux partagent le même écart et les mêmes solutions, car zendriver est un fork communautaire de nodriver. zendriver a un suivi des problèmes plus actif où la question de l'authentification est discutée ouvertement, mais les méthodes fonctionnelles sont identiques. Si vous êtes sur le fork, la configuration spécifique au fork reflète tout ici — le gestionnaire CDP et le whitelisting se comportent de la même manière.

Le résumé honnête : nodriver ne fera pas l'authentification proxy pour vous, et c'est bien une fois que vous connaissez la carte. Whitelistez lorsque votre IP est stable, optez pour le gestionnaire CDP ou l'extension lorsqu'elle ne l'est pas, et gardez un relais dans votre poche arrière pour SOCKS5. Le code ci-dessus répond au défi — mais une sortie résidentielle propre est ce qui vous fait réellement passer la porte.

Exécutez nodriver sur des IP résidentielles whitelisted