Pourquoi Selenium et les Proxies Authentifiés sont un Casse-Tête
Si vous avez déjà essayé de passer des identifiants user:pass à un proxy dans Selenium, vous connaissez la frustration. L'API standard de WebDriver ne supporte pas l'authentification proxy. Chrome affiche une popup de connexion que Selenium ne peut pas gérer. Firefox ignore silencieusement les credentials. C'est un problème bien connu qui pourrit la vie des équipes de scraping et de QA depuis des années.
Les proxies résidentiels Selenium sont indispensables dès que vous scrapez du contenu géo-restreint, surveillez des prix e-commerce, ou exécutez des tests QA distribué. Mais sans authentification, vous ne pouvez tout simplement pas utiliser un fournisseur de proxies payant. Et sans Selenium stealth, vos requêtes se font bloquer par les systèmes anti-bot avant même d'atteindre le contenu.
Dans ce guide, nous couvrons chaque approche production-ready : selenium-wire pour Chrome, les profils Firefox, les bibliothèques anti-détection, le pattern de pool tournant, et la containerisation à l'échelle. Commençons.
Chrome + selenium-wire : Authentification Proxy sans Popup
La bibliothèque selenium-wire étend le WebDriver Chrome en interceptant les requêtes via un proxy MITM local. Le navigateur se connecte au proxy local sans authentification, et selenium-wire relaie le trafic vers votre proxy amont avec les identifiants. Pas de popup, pas de hack.
Installation et configuration de base
pip install selenium-wire selenium
from seleniumwire import webdriver
from selenium.webdriver.chrome.options import Options
# Configuration ProxyHat — proxy résidentiel US avec rotation par requête
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'
}
}
options = Options()
options.add_argument('--disable-blink-features=AutomationControlled')
options.add_experimental_option('excludeSwitches', ['enable-automation'])
driver = webdriver.Chrome(
options=options,
seleniumwire_options=proxy_config
)
driver.get('https://httpbin.org/ip')
print(driver.find_element('tag name', 'body').text)
driver.quit()
Le point clé : selenium-wire agit comme un proxy intermédiaire. Le navigateur envoie du trafic non authentifié à localhost:port_aléatoire, et selenium-wire ajoute l'en-tête Proxy-Authorization avant de transmettre à gate.proxyhat.com:8080. Résultat : zéro popup.
Geo-ciblage et sessions persistantes
ProxyHat supporte le ciblage par pays et par ville directement dans le nom d'utilisateur. Pour une session sticky (même IP pendant toute la session), ajoutez un identifiant de session :
# Session sticky avec IP allemande à Berlin
sticky_proxy = {
'proxy': {
'http': 'http://user-country-DE-city-berlin-session-mySession42:PASSWORD@gate.proxyhat.com:8080',
'https': 'http://user-country-DE-city-berlin-session-mySession42:PASSWORD@gate.proxyhat.com:8080',
}
}
driver = webdriver.Chrome(options=options, seleniumwire_options=sticky_proxy)
# L'IP reste la même tant que le driver est actif
Sans le flag session-, ProxyHat attribue une nouvelle IP résidentielle à chaque requête — idéal pour le scraping massif où vous ne voulez pas que vos requêtes soient corrélées.
Firefox : Génération de Profil Proxy avec Auth Intégrée
Firefox offre une alternative native via son système de profils. Vous pouvez injecter les paramètres proxy directement dans about:config, et pour l'authentification, utiliser un fichier handlers.json ou l'extension Proxy Auth Helper. L'approche la plus robuste reste la création programmatique du profil.
from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options
import tempfile, os, json
def create_firefox_proxy_profile(proxy_host, proxy_port, username, password):
"""Crée un profil Firefox temporaire avec proxy authentifié."""
profile_dir = tempfile.mkdtemp(prefix='ff_proxy_')
# Injection des préférences proxy
prefs_js = 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");
"""
with open(os.path.join(profile_dir, 'user.js'), 'w') as f:
f.write(prefs_js)
# Authentification via le mécanisme d'auto-auth de Firefox
# On crée un fichier de logins chiffré (approche simplifiée via extension)
auth_extension = os.path.join(profile_dir, 'proxy_auth@scraping.xpi')
# En production, utilisez l'extension fx-free-proxy-auth
# ou injectez via marionette / addon
return profile_dir
profile_dir = create_firefox_proxy_profile(
'gate.proxyhat.com', 8080,
'user-country-FR', 'PASSWORD'
)
options = Options()
options.add_argument('-profile')
options.add_argument(profile_dir)
driver = webdriver.Firefox(options=options)
driver.get('https://httpbin.org/ip')
print(driver.find_element('tag name', 'body').text)
driver.quit()
Limitation connue : Firefox ne gère pas nativement l'authentification proxy sans extension tierce. Pour un flux production-ready complet, combinez cette approche avec l'extension Proxy Auth Helper ou utilisez un proxy local sans auth (type mitmproxy) en amont — ce qui nous ramène à la philosophie de selenium-wire.
Selenium Stealth : Réduire les Empreintes d'Automatisation
Même avec un proxy résidentiel parfait, Selenium est triviale à détecter. Les systèmes anti-bot vérifient des dizaines de signaux : navigator.webdriver, les propriétés Chrome CDP, le user-agent, les empreintes Canvas/WebGL, et bien plus. Deux bibliothèques majeures adressent ce problème.
selenium-stealth : Patch rapide pour Chrome
selenium-stealth applique un ensemble de correctifs post-lancement : suppression du flag webdriver, injection de variables Chrome, normalisation du user-agent.
from selenium_stealth import stealth
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless=new')
options.add_argument('--window-size=1920,1080')
driver = webdriver.Chrome(options=options)
stealth(driver,
languages=["fr-FR", "fr", "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/')
# Vérifiez les résultats du test de détection
import time; time.sleep(5)
driver.save_screenshot('stealth_test.png')
driver.quit()
undetected-chromedriver : L'approche la plus robuste
Pour les cibles agressivement protégées, undetected-chromedriver va plus loin : il patche le binaire ChromeDriver lui-même pour supprimer les marqueurs CDP avant le lancement.
import undetected_chromedriver as uc
# Combine avec un proxy résidentiel ProxyHat
options = uc.ChromeOptions()
options.add_argument('--proxy-server=http://gate.proxyhat.com:8080')
# Note : undetected-chromedriver ne gère pas l'auth proxy nativement.
# Combinez avec un proxy local ou selenium-wire pour les credentials.
driver = uc.Chrome(options=options)
driver.get('https://nowsecure.nl/')
print('Titre :', driver.title)
driver.quit()
Conseil pratique : Combinez toujours Selenium stealth avec des proxies résidentiels. Un navigateur « stealth » sur une IP datacenter est toujours suspect. Un navigateur standard sur une IP résidentielle passe souvent. Les deux ensemble maximisent vos chances de succès.
Pattern de Pool de Proxies Tournants
Pour le scraping à grande échelle, vous devez distribuer vos requêtes sur de nombreuses IPs. Le pattern le plus efficace : chaque session WebDriver obtient une IP fraîche via le flag de session ProxyHat, et un gestionnaire de pool recycle les sessions après un nombre défini de requêtes.
import uuid
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from seleniumwire import webdriver as wired_webdriver
from queue import Queue
from threading import Lock
import time
class RotatingProxyPool:
"""Pool de sessions WebDriver avec rotation d'IP résidentielle."""
def __init__(self, max_requests_per_session=50, country='US',
password='PASSWORD', max_pool_size=10):
self.max_requests = max_requests_per_session
self.country = country
self.password = password
self.max_pool_size = max_pool_size
self._pool = Queue(maxsize=max_pool_size)
self._lock = Lock()
self._request_counts = {}
def _create_driver(self):
session_id = str(uuid.uuid4())[:12]
proxy_url = (
f'http://user-country-{self.country}'
f'-session-{session_id}:{self.password}'
f'@gate.proxyhat.com:8080'
)
proxy_opts = {
'proxy': {
'http': proxy_url,
'https': proxy_url,
}
}
options = Options()
options.add_argument('--headless=new')
options.add_argument('--disable-blink-features=AutomationControlled')
driver = wired_webdriver.Chrome(
options=options,
seleniumwire_options=proxy_opts
)
self._request_counts[driver.session_id] = 0
return driver
def acquire(self):
"""Obtient un driver du pool ou en crée un nouveau."""
with self._lock:
if not self._pool.empty():
driver = self._pool.get_nowait()
if self._request_counts.get(driver.session_id, 0) < self.max_requests:
return driver
else:
driver.quit()
return self._create_driver()
def release(self, driver):
"""Remet le driver dans le pool ou le recycle."""
count = self._request_counts.get(driver.session_id, 0) + 1
self._request_counts[driver.session_id] = count
if count >= self.max_requests:
driver.quit()
return
with self._lock:
if self._pool.full():
driver.quit()
else:
self._pool.put_nowait(driver)
def shutdown(self):
while not self._pool.empty():
self._pool.get_nowait().quit()
# Utilisation
pool = RotatingProxyPool(
max_requests_per_session=30,
country='FR',
password='votre_mdp_proxyhat',
max_pool_size=5
)
try:
driver = pool.acquire()
driver.get('https://httpbin.org/ip')
print(driver.find_element('tag name', 'body').text)
pool.release(driver)
finally:
pool.shutdown()
Ce pattern garantit que :
- Chaque session a une IP résidentielle unique via le flag
session- - Les sessions sont recyclées après N requêtes pour éviter la corrélation d'IP
- Le pool est thread-safe pour une utilisation concurrente
- Les ressources sont nettoyées proprement à la fermeture
Selenium Grid et Containerisation : Scraping Parallèle à l'Échelle
Un seul navigateur, c'est lent. Vingt navigateurs en parallèle sur un cluster Docker, c'est de la production. Selenium Grid 4 permet de distribuer vos sessions WebDriver sur un cluster de nœuds Chrome/Firefox, chacun avec son propre proxy.
Architecture Docker Compose
Voici une stack minimaliste avec un hub Selenium et des nœuds Chrome, chaque nœud configuré avec un proxy résidentiel :
# 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-1:
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
# Proxy par défaut pour le nœud
- SE_OPTS=--proxy-url=http://user-country-US:PASSWORD@gate.proxyhat.com:8080
deploy:
replicas: 4
chrome-node-eu:
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_OPTS=--proxy-url=http://user-country-DE-city-berlin:PASSWORD@gate.proxyhat.com:8080
deploy:
replicas: 2
Attention : Selenium Grid en mode standard ne supporte pas l'authentification proxy par session. Pour contourner cette limitation, vous avez deux options :
- Un proxy local par conteneur (ex:
tinyproxyou3proxy) qui relaie vers ProxyHat avec authentification. Le nœud Chrome se connecte au proxy local sans auth. - Des conteneurs séparés par zone géographique, chacun avec son propre proxy amont. C'est l'approche montrée ci-dessus avec
chrome-node-eu.
Client Python pour le Grid
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from concurrent.futures import ThreadPoolExecutor, as_completed
GRID_URL = 'http://localhost:4444/wd/hub'
def scrape_task(url, proxy_country='US'):
options = Options()
options.add_argument(f'--proxy-server=http://gate.proxyhat.com:8080')
# Note : auth via proxy local dans le conteneur
options.add_argument('--headless=new')
options.add_argument('--disable-blink-features=AutomationControlled')
driver = webdriver.Remote(
command_executor=GRID_URL,
options=options
)
try:
driver.get(url)
return {
'url': url,
'title': driver.title,
'ip': driver.execute_script(
'return fetch("https://httpbin.org/ip")'
'.then(r=>r.json()).then(d=>d.origin)'
)
}
finally:
driver.quit()
urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3',
'https://example.com/page4',
'https://example.com/page5',
]
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(scrape_task, url) for url in urls]
for future in as_completed(futures):
result = future.result()
print(f"{result['url']} → IP: {result.get('ip', 'N/A')}")
Selenium vs Playwright : Quand Changer de Framework
Playwright (par Microsoft) est devenu une alternative sérieuse à Selenium. Voici une comparaison honnête pour vous aider à choisir.
| Critère | Selenium | Playwright |
|---|---|---|
| Authentification proxy | Non native — selenium-wire requis | Native via proxy dans BrowserContext |
| Stealth / anti-détection | Extensions tierces (stealth, UC) | Moins de fuites par défaut, mais pas invisible |
| API proxy rotation | Par session WebDriver (lourd) | Par contexte (léger, rapide) |
| Écosystème / intégrations | Massif : Grid, Selenoid, BrowserStack, Sauce Labs | Croissant mais plus restreint |
| Compatibilité legacy | Excellente — projets de 10+ ans | Fraîche — migration nécessaire |
| Parallélisme intégré | Grid séparé requis | asyncio natif + browser contexts |
| Debugging | Outils IDE + VNC sur Grid | Trace Viewer, Codegen, Inspector intégrés |
| Courbe d'apprentissage | Faible si vous connaissez déjà | Faible, API plus moderne |
Quand rester sur Selenium
- Votre infrastructure existante repose sur Selenium Grid / Selenoid et la migration coûterait trop cher.
- Vous utilisez des services cloud (BrowserStack, Sauce Labs, LambdaTest) qui n'ont qu'une API Selenium.
- Votre équipe maîtrise Selenium et le ROI de la migration est faible.
- Vous avez besoin de selenium-wire pour intercepter et modifier les requêtes en vol (headers, payloads).
Quand migrer vers Playwright
- L'authentification proxy est un blocant quotidien — Playwright la gère nativement.
- Vous lancez un nouveau projet sans dette technique.
- La rotation d'IP par contexte (pas par navigateur) est critique pour la performance.
- Vous voulez du parallélisme natif sans gérer un Grid séparé.
Exemple Playwright avec ProxyHat :
browser.new_context(proxy={'server': 'http://gate.proxyhat.com:8080', 'username': 'user-country-US', 'password': 'PASSWORD'})— une seule ligne, zéro hack. C'est dur de ne pas être jaloux.
Bonnes Pratiques de Production
- Toujours valider l'IP avant le scraping cible. Appelez
httpbin.org/ipen premier pour confirmer que le proxy fonctionne. - Respectez les limites de débit. Même avec des proxies résidentiels, un débit de 100+ req/s depuis une seule IP déclenche des CAPTCHAs. Visez 1-3 req/s par session.
- Rottez les user-agents en cohérence avec votre IP géographique. Un UA Chrome Windows sur une IP mobile française est un signal d'anomalie.
- Utilisez des sessions sticky pour les workflows multi-pages (login, panier, checkout). La rotation par requête casse l'état de session.
- Surveillez les taux de succès. Si votre taux tombe sous 85%, changez de pool ou réduisez la concurrence.
- Nettoyez les ressources WebDriver. Un
driver.quit()manquant = un processus Chrome zombie = fuite mémoire.
Points Clés à Retenir
- Selenium proxy auth nécessite selenium-wire (Chrome) ou un profil Firefox + extension — l'API standard ne supporte pas user:pass.
- Les flags
session-etcountry-de ProxyHat dans le username permettent un contrôle fin de la rotation et du géo-ciblage sans configuration supplémentaire.- Selenium stealth est indispensable : combinez undetected-chromedriver ou selenium-stealth avec des proxies résidentiels pour maximiser les taux de succès.
- Le pattern de pool tournant (une IP résidentielle par session, recyclage après N requêtes) est la fondation du scraping fiable à grande échelle.
- Selenium Grid + Docker permet de scaler horizontalement, mais l'authentification proxy par session reste un défi — utilisez des proxies locaux par conteneur.
- Pour les nouveaux projets où l'auth proxy native et le parallélisme sont prioritaires, Playwright est souvent le meilleur choix. Pour l'existant, Selenium reste roi.
Prochaines Étapes
Prêt à construire votre pipeline de scraping résidentiel ? Commencez par configurer une session selenium-wire avec un proxy ProxyHat, testez sur bot.sannysoft.com pour évaluer votre niveau de stealth, puis itérez. Consultez notre guide de scraping pour plus de patterns avancés, ou explorez les tarifs ProxyHat pour trouver le plan adapté à votre volume.






