Reference

Guide complet : Résolution de CAPTCHA, des bases à la production

Résoudre un CAPTCHA par programmation revient toujours à la même boucle : vous extrayez les paramètres du défi, vous les envoyez à une API de résolution, vous interrogez le résultat, puis vous réinjectez le token avant de soumettre le formulaire. Ce guide suit cette boucle jusqu'à la production, avec CaptchaAI et Python.

Comprendre les CAPTCHA et leurs types

Qu'est-ce qu'un CAPTCHA ?

Un CAPTCHA est un défi conçu pour bloquer l'accès automatisé tout en laissant passer les visiteurs humains.

Les types de CAPTCHA que vous allez croiser

Type Exemples Défi
Texte/Image Lettres déformées, expressions mathématiques Saisir ce que vous voyez
Case à cocher reCAPTCHA v2 Cliquer sur la case, résoudre parfois une grille d'images
Invisible reCAPTCHA v3, Turnstile Aucune interaction – notation comportementale
Interactif Curseur GeeTest, grille BLS Faire glisser, cliquer ou ordonner des éléments

CaptchaAI prend en charge reCAPTCHA v2 et v3, Cloudflare Turnstile et Challenge, GeeTest v3, l'image/OCR et les grilles ; hCaptcha et FunCaptcha ne sont pas pris en charge.

Pourquoi les sites déploient des CAPTCHA

  • Empêcher la création automatisée de comptes
  • Bloquer le scraping
  • Couper le spam des formulaires
  • Limiter le débit de l'API

Comment fonctionne un service de résolution de CAPTCHA

Le principe en une boucle

Your Code  →  Submit CAPTCHA to API  →  Solving Service  →  Return Token/Text  →  Your Code Injects Result

Les cinq étapes de la résolution

  1. Extraire les paramètres du défi (sitekey, challenge, image)
  2. Envoyer ces paramètres à l'API de résolution
  3. Interroger le résultat par polling
  4. Injecter le token dans la page
  5. Soumettre le formulaire

Configurer CaptchaAI en Python

Installer les dépendances

pip install requests

Une classe de solveur réutilisable

import time
import requests

class CaptchaAI:
    BASE = "https://ocr.captchaai.com"

    def __init__(self, api_key):
        self.api_key = api_key

    def submit(self, params):
        params["key"] = self.api_key
        params["json"] = 1
        resp = requests.post(f"{self.BASE}/in.php", data=params)
        data = resp.json()
        if data["status"] != 1:
            raise Exception(f"Submit failed: {data['request']}")
        return data["request"]

    def get_result(self, task_id, timeout=300, interval=5, initial_wait=10):
        time.sleep(initial_wait)
        deadline = time.time() + timeout
        while time.time() < deadline:
            resp = requests.get(
                f"{self.BASE}/res.php",
                params={
                    "key": self.api_key,
                    "action": "get",
                    "id": task_id,
                    "json": 1,
                },
            ).json()
            if resp["request"] == "CAPCHA_NOT_READY":
                time.sleep(interval)
                continue
            if resp["status"] == 1:
                return resp["request"]
            raise Exception(f"Solve failed: {resp['request']}")
        raise TimeoutError("Solve timed out")

    def solve(self, params, **kwargs):
        task_id = self.submit(params)
        return self.get_result(task_id, **kwargs)

    def balance(self):
        resp = requests.get(
            f"{self.BASE}/res.php",
            params={"key": self.api_key, "action": "getbalance"},
        )
        return float(resp.text)

Résoudre chaque type de CAPTCHA avec l'API

reCAPTCHA v2

solver = CaptchaAI("YOUR_API_KEY")
token = solver.solve({
    "method": "userrecaptcha",
    "googlekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    "pageurl": "https://example.com/login",
})

reCAPTCHA v3

reCAPTCHA v3 : le score dépend du comportement, prévoyez une attente initiale plus longue.

token = solver.solve({
    "method": "userrecaptcha",
    "googlekey": "SITE_KEY",
    "pageurl": "https://example.com",
    "version": "v3",
    "action": "submit",
}, initial_wait=20)

Cloudflare Turnstile

token = solver.solve({
    "method": "turnstile",
    "sitekey": "0x4AAAAAAAC3a...",
    "pageurl": "https://example.com",
})

GeeTest v3

result = solver.solve({
    "method": "geetest",
    "gt": "GT_VALUE",
    "challenge": "CHALLENGE_VALUE",
    "pageurl": "https://example.com",
})

Image et OCR

import base64

with open("captcha.png", "rb") as f:
    img_b64 = base64.b64encode(f.read()).decode()

text = solver.solve({
    "method": "base64",
    "body": img_b64,
    "numeric": "1",
    "minLen": "4",
    "maxLen": "6",
})

Extraire les paramètres du CAPTCHA depuis la page

Le sitekey reCAPTCHA

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com/login")

# Method 1: From div attribute
sitekey = driver.find_element(
    By.CSS_SELECTOR, "[data-sitekey]"
).get_attribute("data-sitekey")

# Method 2: From iframe URL
import re
iframe = driver.find_element(By.CSS_SELECTOR, "iframe[src*='recaptcha']")
src = iframe.get_attribute("src")
sitekey = re.search(r"k=([^&]+)", src).group(1)

Le sitekey Turnstile

sitekey = driver.find_element(
    By.CSS_SELECTOR, "[data-sitekey], .cf-turnstile"
).get_attribute("data-sitekey")

Les paramètres GeeTest

import json

gt_data = driver.execute_script("""
    return {
        gt: document.querySelector('[data-gt]')?.getAttribute('data-gt'),
        challenge: document.querySelector('[data-challenge]')?.getAttribute('data-challenge')
    };
""")

Injecter la solution dans la page

CAPTCHA basés sur token (reCAPTCHA, Turnstile)

driver.execute_script(f"""
    document.querySelector('[name="g-recaptcha-response"]').value = '{token}';
    document.querySelector('[name="cf-turnstile-response"]').value = '{token}';
""")

Déclencher le callback

Certains sites exigent l'exécution du callback JavaScript, pas seulement un champ rempli. Parcourez alors les clients reCAPTCHA enregistrés :

driver.execute_script(f"""
    if (typeof ___grecaptcha_cfg !== 'undefined') {{
        Object.keys(___grecaptcha_cfg.clients).forEach(function(key) {{
            var client = ___grecaptcha_cfg.clients[key];
            // Find and call the callback
        }});
    }}
""")

Gérer les erreurs et le solde

Logique de retry

Toutes les erreurs ne se valent pas : un solde nul ne se réessaie pas, un CAPTCHA insoluble oui.

def solve_with_retry(solver, params, max_retries=3):
    for attempt in range(max_retries):
        try:
            return solver.solve(params)
        except Exception as e:
            error = str(e)
            if "ZERO_BALANCE" in error:
                raise  # Don't retry — need funds
            if "UNSOLVABLE" in error:
                print(f"Attempt {attempt + 1} failed, retrying...")
                continue
            raise
    raise Exception(f"Failed after {max_retries} attempts")

Surveiller le solde

def check_balance_before_solve(solver, min_balance=0.10):
    balance = solver.balance()
    if balance < min_balance:
        raise Exception(f"Low balance: ${balance:.2f}")
    return balance

Passer à l'échelle : les patterns de production

Prenez un comparateur de prix qui résout des CAPTCHA sur des centaines de pages par heure. CaptchaAI facture au thread : la vraie question est « combien de résolutions en parallèle ? ». BASIC ($15/mois, 5 threads) couvre les petits volumes ; STANDARD ($30/mois, 15 threads) et ADVANCE ($90/mois, 50 threads) ajoutent du parallélisme.

Réutilisation des connexions

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session():
    session = requests.Session()
    retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503])
    adapter = HTTPAdapter(max_retries=retry, pool_connections=10, pool_maxsize=20)
    session.mount("https://", adapter)
    return session

Résolution concurrente

Alignez max_workers sur les threads de votre plan.

from concurrent.futures import ThreadPoolExecutor, as_completed

def solve_batch(solver, captcha_list, max_workers=5):
    results = {}
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(solver.solve, params): url
            for url, params in captcha_list
        }
        for future in as_completed(futures):
            url = futures[future]
            try:
                results[url] = future.result()
            except Exception as e:
                results[url] = f"ERROR: {e}"
    return results

Limitation de débit

import threading

class RateLimiter:
    def __init__(self, max_per_second=10):
        self.interval = 1.0 / max_per_second
        self.lock = threading.Lock()
        self.last_call = 0

    def wait(self):
        with self.lock:
            now = time.time()
            wait_time = self.last_call + self.interval - now
            if wait_time > 0:
                time.sleep(wait_time)
            self.last_call = time.time()

Superviser la résolution en production

Journalisation

Journalisez la méthode et la durée, mais pensez RGPD : pas de données personnelles, seulement des métadonnées techniques.

import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger("captcha")

def solve_logged(solver, params):
    start = time.time()
    logger.info(f"Submitting {params.get('method')} CAPTCHA")
    try:
        result = solver.solve(params)
        elapsed = time.time() - start
        logger.info(f"Solved in {elapsed:.1f}s")
        return result
    except Exception as e:
        elapsed = time.time() - start
        logger.error(f"Failed after {elapsed:.1f}s: {e}")
        raise

Suivi des métriques

class SolveMetrics:
    def __init__(self):
        self.total = 0
        self.success = 0
        self.failures = 0
        self.total_time = 0.0

    def record(self, success, elapsed):
        self.total += 1
        self.total_time += elapsed
        if success:
            self.success += 1
        else:
            self.failures += 1

    def summary(self):
        rate = (self.success / self.total * 100) if self.total else 0
        avg = (self.total_time / self.total) if self.total else 0
        return {
            "total": self.total,
            "success_rate": f"{rate:.1f}%",
            "avg_time": f"{avg:.1f}s",
        }

Récapitulatif : de la première résolution à la production

Étape Tâche
1 Installez requests, récupérez la clé API
2 Identifiez le type de CAPTCHA
3 Extrayez le sitekey
4 Envoyez à CaptchaAI avec la bonne méthode
5 Interrogez le résultat au bon rythme
6 Injectez le token et soumettez le formulaire
7 Ajoutez une logique de retry
8 Surveillez réussite et coûts
9 Montez en charge (pooling, concurrence)

FAQ

Les durées ci-dessous reposent sur des mesures observées ; elles varient selon l'environnement, le volume et l'heure.

CaptchaAI résout-il hCaptcha, FunCaptcha ou GeeTest v4 ?

Non. hCaptcha, FunCaptcha (Arkose Labs) et GeeTest v4 ne sont pas pris en charge — GeeTest v4 est seulement annoncé « à venir ». CaptchaFox, Friendly Captcha et Lemin sont en bêta.

Combien de threads choisir pour mon volume de résolution ?

Autant que de résolutions en parallèle. La facturation est au thread : BASIC ($15/mois, 5 threads) pour un usage léger, ADVANCE ($90/mois, 50 threads) pour un pipeline soutenu.

Pourquoi mon token est-il refusé alors que la résolution a réussi ?

Le plus souvent, il a expiré avant l'envoi ou visait le mauvais champ. Vérifiez le nom du champ (g-recaptcha-response, cf-turnstile-response) et déclenchez le callback si le site l'exige.

Combien de temps un token reste-t-il valide ?

Un token reCAPTCHA reste valide environ 120 secondes, un Turnstile environ 300 secondes. Résolvez à la demande plutôt que d'accumuler des tokens.

Guides connexes

Des bases à la production dans un seul guide —commencer avec CaptchaAI.

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