Selenium Proxy Auth et Stealth : Guide Complet pour le Scraping Résidentiel

Configurez l'authentification proxy dans Selenium avec selenium-wire et Firefox, réduisez les empreintes d'automatisation, et mettez en place un pool de proxies tournants pour le scraping à grande échelle.

Selenium Proxy Auth et Stealth : Guide Complet pour le Scraping Résidentiel

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 :

  1. Un proxy local par conteneur (ex: tinyproxy ou 3proxy) qui relaie vers ProxyHat avec authentification. Le nœud Chrome se connecte au proxy local sans auth.
  2. 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/ip en 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- et country- 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.

Prêt à commencer ?

Accédez à plus de 50M d'IPs résidentielles dans plus de 148 pays avec filtrage IA.

Voir les tarifsProxies résidentiels
← Retour au Blog