Use Cases

Rotation de proxys et CAPTCHA : fiabiliser votre scraping

Votre scraper collecte pendant vingt minutes, puis chaque page renvoie un défi CAPTCHA : ce n'est presque jamais votre code, c'est votre IP qui vient de franchir un seuil. La réponse tient en deux couches — un pool de proxys pour repousser ce seuil, et la résolution par API des défis qui passent quand même. La rotation seule en laisse passer ; la résolution seule paie des défis évitables.

Quel type de proxy choisir selon votre cible

Le type de proxy pèse plus lourd sur votre taux de CAPTCHA que n'importe quel réglage de code.

Type de proxy Cas d'usage typique Taux de CAPTCHA observé Coût relatif
Résidentiel Cibles très protégées (Google, Amazon) Le plus faible $$$
Mobile Détection minimale, petits volumes Le plus faible $$$$
ISP / statique Sessions longues et authentifiées Faible $$
Datacenter Gros volumes sur des sites peu protégés Plus élevé $

En pratique : réservez le résidentiel aux domaines qui déclenchent un défi dès les premières requêtes. Ailleurs, un pool datacenter bien cadencé suffit.

Pourquoi la rotation fait baisser le taux de CAPTCHA

Les protections raisonnent par adresse IP : elles agrègent un historique et déclenchent un défi quand le profil sort de l'ordinaire.

Signal observé par le site Une seule IP Pool en rotation
Requêtes par minute Au-delà de 10, le défi tombe Réparties sur l'ensemble du pool
Réputation de l'IP Se dégrade session après session IP fraîches disponibles en continu
Empreinte de session Un motif unique, facile à isoler Motifs dilués sur plusieurs sorties
Cohérence géographique Un seul point de sortie Diversité géographique naturelle

La rotation ne rend pas votre trafic invisible : elle lui donne de la place. Elle s'accompagne donc toujours d'une limite de débit par IP.

Rotation de proxys en Python : le socle

Le squelette minimal tire un proxy, ouvre une session dédiée et renvoie le token g-recaptcha-response obtenu via l'API.

import requests
import random
import time

PROXIES = [
    "http://user:pass@proxy1.example.com:8080",
    "http://user:pass@proxy2.example.com:8080",
    "http://user:pass@proxy3.example.com:8080",
]

API_KEY = "YOUR_API_KEY"

def get_random_proxy():
    proxy = random.choice(PROXIES)
    return {"http": proxy, "https": proxy}

def scrape_with_rotation(url):
    proxy = get_random_proxy()
    session = requests.Session()
    session.proxies = proxy
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    })

    resp = session.get(url)

    # If CAPTCHA appears, solve it
    if "g-recaptcha" in resp.text or "captcha" in resp.text.lower():
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(resp.text, "html.parser")
        rc = soup.find("div", class_="g-recaptcha")
        if rc:
            site_key = rc["data-sitekey"]
            token = solve_captcha(site_key, url)
            resp = session.post(url, data={"g-recaptcha-response": token})

    return resp.text

def solve_captcha(site_key, page_url):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY, "method": "userrecaptcha",
        "googlekey": site_key, "pageurl": page_url
    })
    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY": continue
        if result.text.startswith("OK|"): return result.text.split("|")[1]
        raise Exception(result.text)
    raise TimeoutError()

Le sitekey se lit dans le DOM, in.php reçoit la tâche, res.php est interrogé jusqu'au token. Bornez la boucle : sans timeout, elle finit par bloquer un worker.

Prioriser les proxys qui déclenchent le moins de défis

Un pool n'est jamais homogène : certaines IP arrivent usées, d'autres tiennent des heures. Tenez un score par proxy et ne tirez que dans la moitié supérieure.

from collections import defaultdict
import random

class SmartProxyRotator:
    def __init__(self, proxies):
        self.proxies = proxies
        self.captcha_count = defaultdict(int)
        self.success_count = defaultdict(int)

    def get_proxy(self):
        # Prefer proxies with lower CAPTCHA rates
        scored = []
        for proxy in self.proxies:
            total = self.captcha_count[proxy] + self.success_count[proxy]
            if total == 0:
                score = 0.5  # Unknown proxy, neutral score
            else:
                score = self.success_count[proxy] / total
            scored.append((proxy, score))

        # Weight selection by score
        scored.sort(key=lambda x: x[1], reverse=True)
        top_proxies = scored[:max(len(scored) // 2, 1)]
        proxy = random.choice(top_proxies)[0]
        return proxy

    def report_success(self, proxy):
        self.success_count[proxy] += 1

    def report_captcha(self, proxy):
        self.captcha_count[proxy] += 1

# Usage
rotator = SmartProxyRotator(PROXIES)

def scrape(url):
    proxy = rotator.get_proxy()
    resp = requests.get(url, proxies={"http": proxy, "https": proxy})

    if "captcha" in resp.text.lower():
        rotator.report_captcha(proxy)
        # Solve CAPTCHA...
    else:
        rotator.report_success(proxy)

    return resp.text

Le taux de défi par proxy est l'indicateur à journaliser en priorité.

Rotation par session avec Selenium

Avec un navigateur, le proxy se fixe au lancement : en changer casse les cookies déjà posés. La granularité utile est un proxy par driver.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def create_driver_with_proxy(proxy_url):
    options = Options()
    options.add_argument(f"--proxy-server={proxy_url}")
    options.add_argument("--disable-blink-features=AutomationControlled")
    return webdriver.Chrome(options=options)

# Rotate proxy per session
proxy = random.choice(PROXIES)
driver = create_driver_with_proxy(proxy)
driver.get("https://example.com")

Fermez le driver entre deux cibles : des cookies de trois domaines derrière quatre IP, c'est le motif que les protections repèrent en premier.

Cloudflare Challenge : le proxy doit suivre le résultat

Pour la plupart des types, le token reste valide quelle que soit l'IP. Pour Cloudflare Challenge, le cookie cf_clearance est lié à l'adresse de sortie : le proxy transmis à l'API doit être celui qui rejouera les requêtes.

proxy = "http://user:pass@proxy.example.com:8080"

resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": API_KEY,
    "method": "cloudflare_challenge",
    "pageurl": "https://example.com",
    "proxy": proxy,
    "proxytype": "HTTP"
})
task_id = resp.text.split("|")[1]

# Poll for cf_clearance cookie
# Use the same proxy for subsequent requests

Cloudflare Challenge se résout en moins de 15 secondes et Cloudflare Turnstile en moins de 10 secondes, avec un taux de réussite élevé sur ces types pris en charge. Épinglez le proxy sur la durée de vie du cookie : si vous en changez, le défi revient.

Dimensionner le pool et le plan CaptchaAI

Cas concret : une équipe lyonnaise suit 10 000 fiches produits par jour chez une dizaine de marchands, depuis des workers hébergés chez OVHcloud. Avec 15 à 20 proxys résidentiels cadencés à 5 requêtes par minute et par IP, seule une faible part des pages renvoie un défi.

La facturation CaptchaAI se fait au thread simultané, en dollars US, résolutions illimitées par thread : BASIC ($15/mois, 5 threads) pour un scraper séquentiel, STANDARD ($30/mois, 15 threads) pour une collecte nocturne, ADVANCE ($90/mois, 50 threads) pour une collecte continue. Dimensionnez sur les défis en vol, pas sur le volume mensuel.

Point souvent oublié : la rotation ne change rien à vos obligations RGPD. Si les pages collectées contiennent des données personnelles, minimisez dès l'ingestion et validez la finalité avec votre référent conformité.

Six règles de cadencement à appliquer

  1. Alignez la géolocalisation sur la cible – des IP françaises pour un site français
  2. Une session par proxy – jamais de cookies rejoués derrière une autre IP
  3. Limitez le débit par proxy – 5 à 10 requêtes par minute et par IP
  4. Surveillez le taux de défi par IP – votre signal d'alerte le plus précoce
  5. Gardez des sessions persistantes – même proxy sur un parcours en plusieurs étapes
  6. Traitez un échec proxy comme un retry – nouvelle IP, backoff exponentiel

Dépannage : symptômes, causes, correctifs

Symptôme Cause probable Correctif
Tout le pool renvoie des défis Cadence trop élevée ou pool usé Passez au résidentiel, divisez le débit par deux
Timeouts sur certaines IP Proxys lents ou saturés Retirez-les du pool, relevez le timeout
Contenu différent selon le proxy Pages géo-spécifiques Verrouillez un seul pays de sortie
Token refusé après résolution Token rejoué depuis une autre session Réutilisez la session d'origine, même proxy pour Cloudflare

FAQ

Les proxys résidentiels sont-ils obligatoires pour scraper ?

Non. Ils sont utiles sur les domaines qui déclenchent un défi dès la deuxième requête ; ailleurs, un pool datacenter cadencé à 5 requêtes par minute et par IP suffit.

Non : il est lié à l'IP qui l'a obtenu. Transmettez le paramètre proxy à la résolution, puis rejouez les requêtes suivantes derrière ce même proxy.

CaptchaAI prend-il en charge hCaptcha sur mes cibles protégées ?

Non — hCaptcha n'est pas pris en charge, pas plus que FunCaptcha (Arkose Labs) ; GeeTest v4 est à venir. Sont couverts reCAPTCHA v2 et v3, Cloudflare Turnstile et Challenge, GeeTest v3, les CAPTCHA image/OCR et les grilles d'images ; CaptchaFox, Friendly Captcha et Lemin sont en bêta.

Quel plan choisir pour une collecte de 10 000 pages par jour ?

Le nombre de défis simultanés compte, pas le volume de pages. Avec une rotation correcte : BASIC ($15/mois, 5 threads) pour démarrer, STANDARD ($30/mois, 15 threads) si plusieurs workers collectent en parallèle.

Faut-il ralentir le scraper quand le taux de défi grimpe ?

Oui, avant toute autre action. Divisez le débit par IP, laissez le pool récupérer, puis remontez par paliers : ajouter des proxys sur une cadence trop agressive use simplement un pool plus large.

Guides connexes

Les commentaires sont désactivés pour cet article.