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
- Extraire les paramètres du défi (sitekey, challenge, image)
- Envoyer ces paramètres à l'API de résolution
- Interroger le résultat par polling
- Injecter le token dans la page
- 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.