Autenticazione Proxy Nodriver: Le Tre Soluzioni che Funzionano

Il risultato principale per l'autenticazione proxy nodriver è una discussione su GitHub, non una guida. nodriver non supporta nativamente user:pass — ecco le tre soluzioni che funzionano, e il trucco in una riga che la maggior parte delle persone ignora.

Cerca autenticazione proxy nodriver e i primi dieci risultati sono una discussione su GitHub, un repository demo, un paio di thread su Stack Overflow riguardanti una libreria diversa e un post su Reddit — nessuna guida reale. La ragione è semplice: nodriver, il successore asincrono di CDP a undetected-chromedriver (quel progetto ha 12.8k stelle su GitHub e 1.3k fork), non ha un modo nativo per passare user:pass a un proxy. Chrome ignora le credenziali incorporate in un flag della riga di comando, e nodriver non lo risolve. Questa guida è la pagina che quella discussione avrebbe dovuto diventare: cosa funziona, cosa no, e le tre soluzioni che fanno funzionare un proxy autenticato.

Il proxy semplice funziona; il proxy autenticato no

Un proxy non autenticato è una riga di codice. Passa l'indirizzo tramite browser_args e ogni richiesta esce dall'IP del 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())

Ora aggiungi le credenziali — --proxy-server=http://USER:PASS@host:port — e si rompe. Chromium rimuove il segmento USER:PASS@ perché il formato del flag non ha uno slot per le credenziali, quindi il proxy risponde con 407 Proxy Authentication Required e Chrome solleva una finestra di dialogo di login nativa che vive fuori dal DOM. nodriver non può vederla o riempirla. Quel 407 è lo stesso muro coperto nella nostra guida per risolvere gli errori 407: il proxy ti sta rifiutando, non il browser. Quindi ogni vera soluzione deve rispondere alla sfida in un altro modo.

Soluzione 1: Whitelist degli IP — nessuna credenziale, nessun dialogo

Questo è il trucco che i thread su GitHub non menzionano mai, ed è il più semplice di gran lunga. Se il tuo scraper funziona da una macchina con un IP pubblico stabile, registra quell'IP nel tuo dashboard del provider e elimina completamente le credenziali — il gateway ti autentica tramite l'indirizzo di origine. Il codice nodriver rimane il semplice snippet --proxy-server sopra, senza alcun codice di autenticazione. Ogni piano QuantumProxies supporta la whitelist degli IP insieme a user:pass sui suoi proxy residenziali, quindi questo è il percorso consigliato ogni volta che il tuo IP di uscita è fisso. Il suo unico limite è topologico: autentica una macchina, non uno script, quindi i runner cloud effimeri, i container dietro NAT e le box CI con IP variabili necessitano di una delle due soluzioni successive.

Confronto di quattro percorsi di autenticazione proxy nodriver: whitelist degli IP, un gestore di autenticazione CDP Fetch, un'estensione Chrome generata e un relay locale
La whitelist non costa codice quando il tuo IP è fisso; il gestore CDP e l'estensione rispondono alla sfida delle credenziali quando non lo è.

Soluzione 2: rispondere alla sfida con un gestore CDP Fetch

nodriver parla direttamente il Chrome DevTools Protocol, quindi puoi intercettare la sfida di autenticazione in-process — nessun file di estensione necessario. Abilita il dominio Fetch con handle_auth_requests=True, quindi rispondi a ogni evento AuthRequired con continue_with_auth. Due dettagli, entrambi dalla risposta nella discussione #1798, fanno la differenza tra funzionare e bloccarsi:

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

Un avvertimento emerso nello stesso thread: un utente ha scoperto che questo funzionava su pagine HTTP semplici ma falliva su HTTPS, e il colpevole era un proxy di bassa qualità, non il codice — passando a un'uscita migliore ha risolto il problema. Questa è la lezione ricorrente dello stealth scraping: il gestore risponde alla sfida, ma la reputazione dell'IP decide se il sito ti lascia entrare.

Soluzione 3: un'estensione Chrome generata

L'altro schema della comunità costruisce una piccola estensione Chrome all'avvio che imposta sia il proxy che risponde alla sfida delle credenziali tramite chrome.webRequest.onAuthRequired — lo stesso trucco che funziona in Selenium e Puppeteer. Scrivi un piccolo manifesto più un worker in background in una directory temporanea e lo carichi tramite --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())

Il manifesto completo e il worker sono identici ai file Manifest V3 nella nostra guida all'autenticazione proxy Selenium — copiali verbatim, solo la chiamata di avvio cambia. Due problemi si ripetono ovunque: le estensioni si caricano solo sotto --headless=new (il semplice --headless fallisce), e una directory non compressa è più affidabile tra le versioni di Chrome rispetto a uno zip compresso. L'estensione gestisce qualsiasi tipo di proxy, il che la rende il ripiego quando il percorso CDP ti sta combattendo.

La quarta opzione: un relay locale

Se preferisci non toccare affatto nodriver, esegui un piccolo relay locale che mantiene le credenziali e presenta un endpoint senza autenticazione su 127.0.0.1. nodriver quindi punta all'indirizzo di loopback con il flag semplice e non vede mai una sfida. Questa è la via più pulita per SOCKS5, dove Chromium rifiuta i proxy autenticati in modo categorico (tracciato come bug di Chromium 40829748). Copriamo il relay minimo, gli strumenti pronti all'uso e quando è eccessivo nella guida al relay proxy.

Una nota su SOCKS5

SOCKS5 non autenticato funziona tramite il flag — --proxy-server=socks5://host:port — ma SOCKS5 autenticato no, e nessun gestore CDP ti salva perché Chromium non ha mai supportato l'autenticazione SOCKS5 con nome utente/password. Le risposte pratiche sono le stesse tre: whitelist l'IP, esegui un relay, o usa l'endpoint HTTP del provider invece. Ogni piano QuantumProxies espone sia proxy HTTP che SOCKS5 sullo stesso gateway, quindi passare all'endpoint HTTP è spesso la soluzione più veloce per SOCKS5. Per i browser anti-detect come famiglia, la mappa dei proxy autenticati confronta nodriver, zendriver e il resto fianco a fianco.

Flusso che mostra come la whitelist degli IP al gateway proxy consente a uno script nodriver di funzionare con un flag proxy semplice e senza dialogo di credenziali
Quando l'IP di uscita è stabile, la whitelist autentica la macchina e il codice di autenticazione scompare completamente.

Domande frequenti

nodriver supporta i proxy autenticati?

Non nativamente. Puoi passare un proxy non autenticato tramite browser_args=["--proxy-server=host:port"], ma user:pass in quel flag viene rimosso da Chromium. Per autenticarti devi o mettere in whitelist il tuo IP con il provider, rispondere alla sfida con un gestore CDP Fetch.AuthRequired, generare un'estensione Chrome per l'autenticazione proxy, o eseguire un relay locale che mantiene le credenziali.

Perché il mio gestore di autenticazione nodriver non riceve eventi?

Quasi sempre perché hai abilitato il dominio Fetch prima di registrare i gestori. La chiamata interna enable di nodriver sovrascrive la registrazione, quindi gli eventi non raggiungono mai il tuo callback. Aggiungi prima i gestori RequestPaused e AuthRequired, poi chiama fetch.enable(handle_auth_requests=True). Inoltre, avvolgi le tue risposte in asyncio.create_task in modo che attenderle non possa bloccare il loop.

nodriver può usare un proxy SOCKS5 con nome utente e password?

No. Chromium non supporta SOCKS5 autenticato (bug di Chromium 40829748), e nodriver eredita quel limite. SOCKS5 non autenticato funziona tramite --proxy-server=socks5://host:port. Per SOCKS5 autenticato, metti in whitelist il tuo IP, esegui un relay locale che aggiunge le credenziali, o passa all'endpoint HTTP del provider, che gestisce l'autenticazione Basic in modo pulito.

nodriver o zendriver per proxy autenticati?

Entrambi condividono lo stesso gap e le stesse soluzioni, perché zendriver è un fork della comunità di nodriver. zendriver ha un tracker di problemi più attivo dove la questione dell'autenticazione è discussa apertamente, ma i metodi funzionanti sono identici. Se sei sul fork, la configurazione specifica del fork rispecchia tutto qui — il gestore CDP e la whitelist si comportano allo stesso modo.

Il riassunto onesto: nodriver non farà l'autenticazione proxy per te, e va bene una volta che conosci la mappa. Metti in whitelist quando il tuo IP è stabile, opta per il gestore CDP o l'estensione quando non lo è, e tieni un relay nel tuo arsenale per SOCKS5. Il codice sopra risponde alla sfida — ma un'uscita residenziale pulita è ciò che effettivamente ti fa passare la porta.

Esegui nodriver su IP residenziali in whitelist