La règle tient en une phrase : traitez chaque CAPTCHA de la page comme une tâche indépendante. Chaque widget possède sa propre clé de site, son propre textarea g-recaptcha-response et son propre callback ; les confondre est la première cause de formulaires qui restent bloqués même après l'injection d'un token valide.
Le cas est courant. On le rencontre notamment sur :
- une page de connexion sous reCAPTCHA qui affiche aussi une inscription newsletter protégée par un second reCAPTCHA ;
- un tunnel de paiement en plusieurs étapes où chaque étape déclenche son propre défi.
La démarche ne change pas, quel que soit le nombre de widgets. Le reste de ce guide suit ces trois étapes :
- Détecter tous les widgets de la page et lire leurs clés de site.
- Résoudre les tokens en parallèle via l'API CaptchaAI.
- Réinjecter chaque token dans le bon widget, puis déclencher son callback.
Étape 1 : détecter tous les widgets de la page
Recensez tous les widgets avant de résoudre quoi que ce soit. Deux approches se complètent :
- Parcourir les iframes reCAPTCHA (
iframe[src*="recaptcha/api2/anchor"]) et lire la clé dans le paramètrek=. - Lister les conteneurs
.g-recaptchaet lire leur attributdata-sitekey.
Attendez toujours le rendu complet de la page (
WebDriverWait,page.waitForSelector) avant de scanner : un widget chargé en lazy loading passerait inaperçu.
Détection en Python avec Selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/multi-captcha-page")
# Find all reCAPTCHA iframes
captcha_iframes = driver.find_elements(
By.CSS_SELECTOR, 'iframe[src*="recaptcha/api2/anchor"]'
)
# Extract sitekeys from each iframe's src
import re
captchas = []
for i, iframe in enumerate(captcha_iframes):
src = iframe.get_attribute("src")
match = re.search(r"k=([A-Za-z0-9_-]+)", src)
if match:
captchas.append({
"index": i,
"sitekey": match.group(1),
"iframe": iframe
})
print(f"Found {len(captchas)} CAPTCHAs on page")
for c in captchas:
print(f" [{c['index']}] sitekey: {c['sitekey']}")
Résultat attendu :
Found 2 CAPTCHAs on page
[0] sitekey: 6LcXyzABCDEF-login
[1] sitekey: 6LcAbcDEFGHI-signup
Détection en JavaScript avec Puppeteer
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/multi-captcha-page', {
waitUntil: 'networkidle2'
});
// Extract all reCAPTCHA widgets from the page
const captchas = await page.evaluate(() => {
const widgets = document.querySelectorAll('.g-recaptcha');
return Array.from(widgets).map((el, i) => ({
index: i,
sitekey: el.getAttribute('data-sitekey'),
elementId: el.id || `captcha-${i}`,
callbackName: el.getAttribute('data-callback') || null
}));
});
console.log(`Found ${captchas.length} CAPTCHAs`);
captchas.forEach(c => console.log(` [${c.index}] ${c.sitekey}`));
})();
Étape 2 : résoudre les CAPTCHA en parallèle
Envoyez toutes les clés de site à CaptchaAI en une seule salve, puis interrogez l'API jusqu'à ce que chaque token revienne. Ne bloquez jamais sur un CAPTCHA avant de lancer le suivant : c'est le parallélisme qui rend l'opération viable.
Les durées évoquées ici reposent sur des mesures observées et des retours d'utilisateurs. Les résultats varient selon l'environnement, le volume et le moment de la journée.
Prenons un tunnel d'inscription francophone : l'écran de création de compte impose un reCAPTCHA, puis l'écran de consentement RGPD qui suit en ajoute un second. La capacité se raisonne en threads :
- Chaque CAPTCHA en cours de résolution occupe un thread ; il se libère dès que le token revient.
- Cinq widgets en parallèle mobilisent donc cinq threads simultanés.
- Le plan BASIC ($15/mois, 5 threads) couvre déjà ce cas ; STANDARD ($30/mois, 15 threads) laisse de la marge.
- La facturation dépend du nombre de threads, jamais du nombre de résolutions.
Python
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = "YOUR_API_KEY"
PAGE_URL = "https://example.com/multi-captcha-page"
def submit_captcha(sitekey):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": PAGE_URL,
"json": "1"
})
result = resp.json()
if result["status"] != 1:
raise Exception(f"Submit error: {result['request']}")
return result["request"]
def poll_result(task_id, timeout=120):
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1"
})
result = resp.json()
if result["status"] == 1:
return result["request"]
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Poll error: {result['request']}")
raise TimeoutError(f"Task {task_id} timed out")
def solve_all(captchas):
# Submit all in parallel
task_ids = {}
with ThreadPoolExecutor(max_workers=len(captchas)) as pool:
futures = {
pool.submit(submit_captcha, c["sitekey"]): c["index"]
for c in captchas
}
for future in as_completed(futures):
idx = futures[future]
task_ids[idx] = future.result()
print(f"[captcha-{idx}] Submitted → task {task_ids[idx]}")
# Poll all in parallel
tokens = {}
with ThreadPoolExecutor(max_workers=len(task_ids)) as pool:
futures = {
pool.submit(poll_result, tid): idx
for idx, tid in task_ids.items()
}
for future in as_completed(futures):
idx = futures[future]
tokens[idx] = future.result()
print(f"[captcha-{idx}] Solved")
return tokens
# Example usage
captchas = [
{"index": 0, "sitekey": "6LcXyzABCDEF-login"},
{"index": 1, "sitekey": "6LcAbcDEFGHI-signup"}
]
tokens = solve_all(captchas)
Résultat attendu :
[captcha-0] Submitted → task 71823456
[captcha-1] Submitted → task 71823457
[captcha-1] Solved
[captcha-0] Solved
Étape 3 : injecter chaque token dans le bon widget
Chaque widget reCAPTCHA gère son propre textarea g-recaptcha-response, imbriqué dans son conteneur. Trois règles évitent les erreurs les plus fréquentes :
- Visez le
textareadu bon conteneur, jamais le premier venu de la page. - Associez chaque token à une clé de site, pas à une position d'index.
- Déclenchez le callback (
data-callback) après avoir écrit le token, sinon le formulaire reste bloqué.
Injection en Python (Selenium)
def inject_tokens(driver, captchas, tokens):
for c in captchas:
idx = c["index"]
token = tokens[idx]
# Find the textarea within the widget's container
container = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")[idx]
textarea = container.find_element(
By.CSS_SELECTOR, 'textarea[name="g-recaptcha-response"]'
)
driver.execute_script(
"arguments[0].value = arguments[1];", textarea, token
)
# Trigger the callback if defined
callback = c.get("callback")
if callback:
driver.execute_script(f"{callback}('{token}');")
print(f"[captcha-{idx}] Token injected")
inject_tokens(driver, captchas, tokens)
Injection en JavaScript (Puppeteer)
async function injectTokens(page, captchas, tokens) {
for (const captcha of captchas) {
const token = tokens[captcha.index];
await page.evaluate((idx, tkn, callbackName) => {
const widgets = document.querySelectorAll('.g-recaptcha');
const textarea = widgets[idx].querySelector(
'textarea[name="g-recaptcha-response"]'
);
textarea.value = tkn;
if (callbackName && typeof window[callbackName] === 'function') {
window[callbackName](tkn);
}
}, captcha.index, token, captcha.callbackName);
console.log(`[captcha-${captcha.index}] Token injected`);
}
}
await injectTokens(page, captchas, tokens);
Pages mixtes : reCAPTCHA et Turnstile ensemble
Quand une page combine plusieurs technologies — par exemple reCAPTCHA et Cloudflare Turnstile côte à côte — détectez chaque famille séparément avant de soumettre :
def detect_all_captchas(driver):
detected = []
# reCAPTCHA
recaptchas = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")
for i, el in enumerate(recaptchas):
detected.append({
"type": "userrecaptcha",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"recaptcha-{i}"
})
# Turnstile
turnstiles = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile")
for i, el in enumerate(turnstiles):
detected.append({
"type": "turnstile",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"turnstile-{i}"
})
return detected
Envoyez ensuite chaque tâche avec le paramètre method qui lui correspond :
- reCAPTCHA →
method=userrecaptcha, token réinjecté dansg-recaptcha-response. - Cloudflare Turnstile →
method=turnstile, token réinjecté danscf-turnstile-response.
Ne mélangez jamais les deux champs : chaque type de CAPTCHA possède son propre nom de token.
Bonnes pratiques pour les pages multi-CAPTCHA
- Journalisez chaque widget (index, clé de site, callback) : c'est ce qui permet de tracer un échec précis.
- Re-scannez la page après chaque soumission de formulaire ; un second défi peut n'apparaître qu'ensuite.
- Minimisez les données personnelles manipulées dans ces formulaires, conformément à vos obligations RGPD.
- Prévoyez un retry avec backoff exponentiel si un token expire avant l'envoi du formulaire.
Dépannage
| Problème | Cause | Correctif |
|---|---|---|
| Token injecté mais formulaire toujours bloqué | Callback jamais déclenché | Lisez data-callback et appelez la fonction avec le token |
| Seul le premier CAPTCHA est détecté | Le second widget se charge en lazy loading | Attendez l'apparition de toutes les iframes avant de scanner la page |
| Token placé dans le mauvais widget | Décalage d'index | Associez chaque token à une clé de site, jamais à une position |
ERROR_WRONG_GOOGLEKEY |
Clé de site mal extraite | Relisez la clé depuis l'attribut src de l'iframe ou data-sitekey |
FAQ
Combien de threads faut-il pour résoudre plusieurs CAPTCHA en parallèle ?
Un par CAPTCHA en vol. Trois widgets résolus simultanément occupent trois threads le temps de la résolution ; dès qu'un token revient, le thread se libère. Le plan BASIC ($15/mois, 5 threads) suffit pour la plupart des pages multi-CAPTCHA.
CaptchaAI peut-il gérer une page mêlant reCAPTCHA et Cloudflare Turnstile ?
Oui. Détectez chaque famille avec son sélecteur, puis soumettez chaque tâche avec le bon paramètre method :
.g-recaptcha→userrecaptcha.cf-turnstile→turnstile
Les deux types se résolvent dans la même salve parallèle.
Pourquoi mon formulaire reste-t-il bloqué après l'injection du token ?
Le plus souvent, le callback n'a pas été déclenché. Écrire le token dans le textarea g-recaptcha-response ne suffit pas si la page attend un data-callback : récupérez le nom de la fonction et appelez-la explicitement avec le token.
Faut-il résoudre les CAPTCHA un par un ou simultanément ?
Simultanément. Chaque résolution prend de l'ordre de 15 à 30 s ; en série, les délais s'additionnent, alors qu'en parallèle le temps total reste proche de celui d'un seul défi.
Résolvez autant de CAPTCHA que nécessaire par page
Créez votre compte et récupérez votre clé API sur captchaai.com, puis lancez votre première salve parallèle.