Perché Selenium Proxy Auth è un problema aperto
Se hai mai provato a passare un proxy con autenticazione a Selenium, sai che il dolore è reale. Il ChromeOptions.add_argument('--proxy-server=http://user:pass@host:port') viene semplicemente ignorato da Chromium: il browser non inoltra le credenziali e la richiesta muore con un 407 Proxy Authentication Required. Per i team di QA e scraping che lavorano con Selenium residential proxies, questo è il primo muro che incontrano.
Il problema è architetturale: il protocollo HTTP proxy auth (RFC 7235) prevede un challenge-response che i driver Selenium non gestiscono nativamente. Le soluzioni spaziano dall'uso di estensioni custom a librerie wrapper che intercettano il traffico a livello di middleware. In questa guida esploreremo ogni approccio con codice funzionante, integrando Selenium stealth per evitare il fingerprinting, e pattern di rotazione IP per operare a scala.
Chrome + selenium-wire: il middleware che risolve l'autenticazione
selenium-wire è un drop-in replacement per Selenium WebDriver che estende le capacità del driver intercettando tutto il traffico tramite un proxy locale MITM. Il vantaggio fondamentale: gestisce l'autenticazione proxy in modo trasparente, iniettando l'header Proxy-Authorization in ogni richiesta upstream.
Installazione e configurazione base
pip install selenium-wire selenium
from seleniumwire import webdriver
from selenium.webdriver.chrome.options import Options
opts = Options()
opts.add_argument("--headless=new")
opts.add_argument("--disable-gpu")
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=opts, seleniumwire_options=proxy_config)
driver.get("https://httpbin.org/ip")
print(driver.page_source)
driver.quit()
selenium-wire crea un proxy locale sulla porta 8080 (configurabile) che fa da intermediario. Il traffico dal browser viaggia senza auth al proxy locale, che poi inoltra con le credenziali al proxy upstream. Questo risolve completamente il problema della Selenium proxy auth senza estensioni Chrome o workarounds fragili.
Geo-targeting granulare con ProxyHat
Le residential proxy di ProxyHat supportano targeting per paese e città direttamente nel username. Questo è particolarmente utile per test QA localizzati o scraping di contenuti geo-restrittti:
# Targeting Germania, Berlino
proxy_config = {
"proxy": {
"http": "http://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:8080",
"https": "http://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:8080",
}
}
# Sessione sticky per mantenere lo stesso IP
proxy_config = {
"proxy": {
"http": "http://user-country-DE-session-mySession123:PASSWORD@gate.proxyhat.com:8080",
"https": "http://user-country-DE-session-mySession123:PASSWORD@gate.proxyhat.com:8080",
}
}
Ispezione del traffico intercettato
Un beneficio collaterale di selenium-wire: puoi ispezionare richieste e risposte, utile per debugging e validazione QA:
driver.get("https://example.com")
for request in driver.requests:
if request.response:
print(f"{request.method} {request.url} -> {request.response.status_code}")
if "proxy-authorization" in request.headers:
print(f" Proxy auth header presente")
Firefox: generazione programmatica del profilo proxy
Firefox gestisce i proxy diversamente da Chrome: le configurazioni proxy vivono nel file prefs.js del profilo. A differenza di Chrome, Firefox supporta l'autenticazione proxy nativamente tramite le preferenze network.proxy.*, ma solo per proxy senza auth. Per i proxy autenticati, serve un approccio diverso: l'estensione Proxy Auth Helper o la configurazione di un proxy locale.
Profilo Firefox con proxy autenticato
from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from tempfile import mkdtemp
import os
import json
# Crea un profilo temporaneo
profile_dir = mkdtemp()
prefs_path = os.path.join(profile_dir, "user.js")
# Configura il proxy SOCKS5 (Firefox supporta auth SOCKS5 nativamente)
# Per HTTP proxy autenticati, usiamo l'estensione approach
proxy_host = "gate.proxyhat.com"
proxy_port = 1080 # SOCKS5
proxy_user = "user-country-US"
proxy_pass = "PASSWORD"
prefs_content = f"""// Proxy configuration
user_pref("network.proxy.type", 1);
user_pref("network.proxy.socks", "{proxy_host}");
user_pref("network.proxy.socks_port", {proxy_port});
user_pref("network.proxy.socks_username", "{proxy_user}");
user_pref("network.proxy.socks_password", "{proxy_pass}");
user_pref("network.proxy.socks_remote_dns", true);
user_pref("network.proxy.no_proxies_on", "localhost, 127.0.0.1");
"""
with open(prefs_path, "w") as f:
f.write(prefs_content)
# Crea il profilo e lancia il driver
profile = webdriver.FirefoxProfile(profile_dir)
opts = webdriver.FirefoxOptions()
opts.profile = profile
opts.add_argument("--headless")
service = Service("geckodriver")
driver = webdriver.Firefox(service=service, options=opts)
driver.get("https://httpbin.org/ip")
print(driver.page_source)
driver.quit()
L'approccio SOCKS5 è più pulito in Firefox perché il browser gestisce nativamente le credenziali SOCKS5. Per gli Selenium residential proxies su HTTP, il pattern con selenium-wire rimane la scelta più robusta anche per Firefox.
Selenium Stealth: ridurre il fingerprinting dell'automazione
Anche con il proxy giusto, Selenium viene spesso rilevato dai sistemi anti-bot. Il motivo: il WebDriver inietta proprietà JavaScript come navigator.webdriver=true, modifica navigator.plugins, e altera il comportamento di Permissions API e Chrome DevTools Protocol. I servizi di Selenium stealth mirano a neutralizzare questi segnali.
selenium-stealth: patch del browser in volo
selenium-stealth applica una serie di patch al browser dopo l'avvio per rimuovere o camuffare i marker di automazione più comuni:
pip install selenium-stealth
from selenium import webdriver
from selenium_stealth import stealth
opts = webdriver.ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=opts)
# Applica stealth patches
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
# Configura il proxy via selenium-wire o estensione
# (combina con selenium-wire per auth + stealth)
driver.get("https://bot.sannysoft.com")
print("Stealth test completato")
driver.quit()
Combinare selenium-wire + selenium-stealth
Il pattern più potente per il scraping production-grade è combinare entrambe le librerie. selenium-wire gestisce l'autenticazione proxy, selenium-stealth riduce il fingerprinting:
from seleniumwire import webdriver as sw_webdriver
from selenium_stealth import stealth
opts = sw_webdriver.ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--disable-blink-features=AutomationControlled")
proxy_config = {
"proxy": {
"http": "http://user-country-US:PASSWORD@gate.proxyhat.com:8080",
"https": "http://user-country-US:PASSWORD@gate.proxyhat.com:8080",
}
}
driver = sw_webdriver.Chrome(options=opts, seleniumwire_options=proxy_config)
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
driver.get("https://nowsecure.nl")
# Verifica che non venga rilevato
print("Pagina caricata con proxy autenticato + stealth")
driver.quit()
selenium-driverless: l'approccio CDP nativo
selenium-driverless è un'alternativa più recente che comunica direttamente via Chrome DevTools Protocol senza passare per il WebDriver tradizionale. Questo elimina alla radice molti marker di automazione perché non esiste un endpoint WebDriver esposto. L'approccio è promettente ma meno maturo di selenium-stealth per scenari di produzione critici.
Pattern: rotating proxy pool per WebDriver sessioni
Per lo scraping a scala, un singolo IP non basta. Serve un pattern dove ogni sessione WebDriver ottiene un IP fresco dal pool di Selenium residential proxies. Con ProxyHat, la rotazione può avvenire a due livelli:
- Per-request: ogni richiesta HTTP ottiene un IP diverso (rotazione automatica, nessun flag session necessario).
- Per-session: ogni WebDriver mantiene lo stesso IP per tutta la durata della sessione (usando il flag
session-nell'username).
Rotazione per-sessione con factory pattern
import itertools
import uuid
from seleniumwire import webdriver as sw_webdriver
from selenium_stealth import stealth
from contextlib import contextmanager
PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER_PREFIX = "user-country-US"
PROXY_PASS = "PASSWORD"
@contextmanager
def create_stealth_driver(country="US", city=None, sticky=True):
"""Factory che crea un driver con IP fresco ad ogni sessione."""
session_id = uuid.uuid4().hex[:12] if sticky else None
username_parts = [f"user-country-{country}"]
if city:
username_parts.append(f"city-{city}")
if session_id:
username_parts.append(f"session-{session_id}")
username = "-".join(username_parts)
proxy_url = f"http://{username}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
proxy_config = {
"proxy": {
"http": proxy_url,
"https": proxy_url,
}
}
opts = sw_webdriver.ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--disable-blink-features=AutomationControlled")
opts.add_argument("--window-size=1920,1080")
driver = sw_webdriver.Chrome(options=opts, seleniumwire_options=proxy_config)
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
try:
yield driver
finally:
driver.quit()
# Utilizzo: ogni contesto ha il proprio IP
with create_stealth_driver(country="DE", city="berlin") as driver:
driver.get("https://httpbin.org/ip")
print(f"IP tedesco: {driver.page_source}")
# Sessione successiva = IP diverso
with create_stealth_driver(country="US") as driver:
driver.get("https://httpbin.org/ip")
print(f"IP americano: {driver.page_source}")
Questo pattern è particolarmente efficace per lo scraping SERP e il monitoraggio dei prezzi: ogni categoria o mercato geografico ottiene la propria sessione con IP residente locale. La chiusura del driver nel finally garantisce che le risorse vengano rilasciate anche in caso di eccezioni.
Selenium Grid e containerizzazione per scraping parallelo
Quando il volume cresce, un singolo nodo non basta. Selenium Grid 4 permette di distribuire le esecuzioni su più nodi in parallelo, ciascuno con il proprio proxy e la propria identità. La containerizzazione con Docker rende il deployment ripetibile e scalabile.
Architettura di un fleet headless
Il pattern consigliato per lo scraping production-grade:
- Job Queue (Redis, RabbitMQ, o SQS): contiene gli URL da processare.
- Worker Pool: N container Docker, ognuno con Chrome + selenium-wire + selenium-stealth.
- Proxy Rotation Layer: ogni worker estrae un IP fresco dal pool ProxyHat all'avvio della sessione.
- Result Store: S3, GCS, o database per i risultati.
- Monitoring: metriche su success rate, latenza, e tasso di CAPTCHA.
Docker Compose per Selenium Grid + proxy
# docker-compose.yml
version: '3.8'
services:
selenium-hub:
image: selenium/hub:4.18
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
depends_on:
- selenium-hub
deploy:
replicas: 4
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
- SE_VNC_NO_PASSWORD=1
# Il proxy va configurato nel codice, non a livello di container
scraper-worker:
build: ./scraper
depends_on:
- selenium-hub
deploy:
replicas: 4
environment:
- HUB_URL=http://selenium-hub:4444/wd/hub
- PROXY_HOST=gate.proxyhat.com
- PROXY_PORT=8080
- PROXY_USER_PREFIX=user-country-US
- PROXY_PASSWORD=${PROXYHAT_PASSWORD}
Worker che si connette al Grid
import os
import uuid
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium_stealth import stealth
HUB_URL = os.environ["HUB_URL"]
PROXY_HOST = os.environ["PROXY_HOST"]
PROXY_PORT = os.environ["PROXY_PORT"]
PROXY_USER = f"{os.environ['PROXY_USER_PREFIX']}-session-{uuid.uuid4().hex[:10]}"
PROXY_PASS = os.environ["PROXY_PASSWORD"]
# Nota: con Selenium Grid remoto, selenium-wire non funziona direttamente
# perché il proxy locale del worker non è raggiungibile dal nodo Chrome.
# Soluzione: usare l'estensione Chrome per proxy auth.
def create_proxy_auth_extension(proxy_host, proxy_port, proxy_user, proxy_pass):
"""Genera estensione Chrome per proxy auth (workaround per Grid remoto)."""
import zipfile, tempfile
manifest_json = '{"version":"1.0.0","manifest_version":2,"name":"Proxy Auth","permissions":["webRequest","webRequestBlocking","<all_urls>"],"background":{"scripts":["background.js"]}}'
background_js = f'''
var auth = {{}};
auth["{proxy_host}:{proxy_port}"] = {{username: "{proxy_user}", password: "{proxy_pass}"}};
chrome.webRequest.onAuthRequired.addListener(
function(details) {{
if (auth[details.challenger.host + ":" + details.challenger.port]) {{
return {{authCredentials: auth[details.challenger.host + ":" + details.challenger.port]}};
}}
}},
{{urls: ["<all_urls>"]}},
["blocking"]
);'''
pluginfile = tempfile.mktemp(suffix=".zip")
with zipfile.ZipFile(pluginfile, 'w') as zp:
zp.writestr("manifest.json", manifest_json)
zp.writestr("background.js", background_js)
return pluginfile
opts = Options()
opts.add_argument("--headless=new")
opts.add_argument("--disable-blink-features=AutomationControlled")
proxy_ext = create_proxy_auth_extension(PROXY_HOST, PROXY_PORT, PROXY_USER, PROXY_PASS)
opts.add_extension(proxy_ext)
opts.add_argument(f"--proxy-server=http://{PROXY_HOST}:{PROXY_PORT}")
driver = webdriver.Remote(
command_executor=HUB_URL,
options=opts
)
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
print("Driver remoto con proxy autenticato avviato")
driver.quit()
Il limite di selenium-wire con Selenium Grid è che il proxy MITM locale gira sul worker, non sul nodo Chrome. L'estensione Chrome per proxy auth è il workaround più affidabile per i nodi remoti. È più fragile di selenium-wire, ma funziona in architetture distribuite.
Quando preferire Playwright a Selenium
Playwright di Microsoft è diventato una valida alternativa a Selenium, specialmente per i nuovi progetti di scraping. Ecco un confronto onesto per aiutarti a scegliere:
| Criterio | Selenium | Playwright |
|---|---|---|
| Proxy auth nativo | No (serve selenium-wire o estensione) | Sì (parametro proxy nel browser context) |
| Stealth integrato | No (serve selenium-stealth o driverless) | Parziale (nessun navigator.webdriver, ma rilevabile) |
| API modernità | Legacy, verbosa | Moderna, async-first, auto-wait |
| Ecosistema e community | Enorme, 20+ anni, tooling maturo | In crescita, ma più piccolo |
| Compatibilità browser | Chrome, Firefox, Edge, Safari | Chrome, Firefox, WebKit |
| Grid / distribuzione | Selenium Grid 4 maturo | Playwright Test sharding, ma meno flessibile |
| Integrazione QA esistente | Profonda (Sauce Labs, BrowserStack, ecc.) | In crescita, ma non paragonabile |
| Intercept traffico | selenium-wire (esterno) | Route API nativa (page.route()) |
Playwright con ProxyHat: l'alternativa più pulita
Se non hai vincoli di legacy, Playwright offre un'esperienza nettamente superiore per il proxy auth:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
proxy={
"server": "http://gate.proxyhat.com:8080",
"username": "user-country-DE-city-berlin",
"password": "PASSWORD",
}
)
page = browser.new_page()
page.goto("https://httpbin.org/ip")
print(page.content())
browser.close()
Niente estensioni, niente selenium-wire, niente workaround. L'auth è gestita nativamente dal browser context. Per i nuovi progetti di scraping, questa è la ragione principale per scegliere Playwright.
Quando restare con Selenium
- Hai una suite QA esistente con migliaia di test Selenium.
- La tua infrastruttura CI/CD è integrata con Sauce Labs, BrowserStack o servizi simili.
- Hai bisogno di Safari (Playwright usa WebKit, non Safari nativo).
- Il tuo team ha competenze profonde in Selenium e la migrazione non giustifica il ROI.
Quando migrare a Playwright
- Stai costruendo un nuovo progetto di scraping da zero.
- L'auth proxy è un requisito centrale e selenium-wire introduce troppa complessità.
- Vuoi auto-wait nativo e un'API più concisa.
- La tua suite QA è piccola e la migrazione è fattibile.
Best practice per scraping production-grade
Gestione delle CAPTCHA
Anche con Selenium stealth e residential proxy, incontrerai CAPTCHA. Le strategie più efficaci:
- Rate limiting intelligente: non superare 3-5 richieste per minuto per IP residente. I residential proxy di ProxyHat ruotano automaticamente se non usi il flag
session-. - Pattern di navigazione umana: aggiungi delay casuali, scroll, e hover prima di interagire con gli elementi.
- Fallback strategy: quando rilevi una CAPTCHA, salva l'URL in una retry queue e passa al prossimo task.
- Risoluzione CAPTCHA: per i casi in cui è inevitabile, integra servizi come 2Captcha o CapSolver via API.
Monitoraggio e metriche
Per operare a scala, devi misurare:
- Success rate: percentuale di richieste che restituiscono dati validi (target: >95%).
- Latenza P50/P95: i residential proxy hanno latenza più alta dei datacenter (3-8s vs 0.5-1s).
- CAPTCHA rate: se supera il 10%, riduci la concorrenza o migliora lo stealth.
- IP freshness: traccia quanti IP unici usi per sessione per verificare che la rotazione funzioni.
Considerazioni etiche e legali
Il scraping web esiste in una zona grigia. Rispetta sempre:
- robots.txt: controlla sempre prima di scrapear un sito.
- Termini di servizio: molti siti proibiscono lo scraping esplicito.
- GDPR e CCPA: non raccogliere dati personali senza base legale.
- Rate limiting ragionevole: non sovraccaricare i server altrui.
Regola d'oro: se il tuo scraping causasse un impatto significativo sul server target, stai esagerando. Usa la concorrenza minima necessaria per completare il lavoro.
Punti chiave
- Selenium non supporta proxy auth nativamente: selenium-wire è la soluzione più robusta per Chrome, l'estensione Chrome per Selenium Grid remoto.
- Firefox + SOCKS5 è l'approccio più pulito per il proxy auth nativo nel browser, usando la porta 1080 di ProxyHat.
- selenium-stealth + selenium-wire è la combinazione vincente per scraping con proxy autenticato e fingerprint ridotto.
- Rotazione per-sessione con il flag
session-di ProxyHat garantisce IP freschi per ogni WebDriver senza perdere coerenza durante la sessione. - Selenium Grid + Docker permette di scalare orizzontalmente, ma richiede l'estensione Chrome per proxy auth sui nodi remoti.
- Playwright è superiore per nuovi progetti: proxy auth nativo, API moderna, auto-wait. Selenium resta la scelta per legacy e compatibilità QA.
Per esplorare le opzioni di proxy residenziali, mobile e datacenter compatibili con Selenium, visita la pagina dei prezzi di ProxyHat o consulta le locazioni disponibili. Per approfondire i pattern di scraping, leggi la nostra guida sui proxy residenziali per web scraping.






