Certaines pages affichent deux CAPTCHA ou plus : un formulaire de connexion avec reCAPTCHA et une inscription à la newsletter avec un deuxième reCAPTCHA, ou un formulaire en plusieurs étapes où chaque étape déclenche son propre défi. Chaque CAPTCHA a une clé de site et un élément cible uniques, vous devez donc tous les détecter, les résoudre en parallèle et injecter chaque jeton dans le rappel correct.
Détection de plusieurs CAPTCHA
Python avec Sélénium
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
JavaScript avec Marionnettiste
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}`));
})();
Résoudre tous les CAPTCHA en parallèle
Soumettez tous les CAPTCHA à CaptchaAI en même temps, puis interrogez jusqu'à ce que chacun soit résolu.
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
Injecter des jetons dans le bon widget
Chaque widget reCAPTCHA possède sa propre zone de texte g-recaptcha-response. Lorsqu'il existe plusieurs widgets, chaque zone de texte est imbriquée dans son conteneur de widgets.
Python (sélénium)
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)
JavaScript (Marionnette)
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);
Types CAPTCHA mixtes
Lorsqu'une page comporte différents types de CAPTCHA (par exemple, reCAPTCHA + Turnstile), détectez chaque type séparément :
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
Soumettez chacun avec son paramètre method correspondant : userrecaptcha pour reCAPTCHA, turnstile pour Turnstile.
Dépannage
| Problème | Parce que | Corriger |
|---|---|---|
| Jeton injecté mais formulaire toujours bloqué | Rappel non déclenché | Vérifiez data-callback et appelez-le avec le jeton |
| Seul le premier CAPTCHA détecté | Le deuxième CAPTCHA se charge paresseusement | Attendez que toutes les iframes/widgets apparaissent avant de numériser |
| Mauvais jeton dans le mauvais widget | Incompatibilité d'index | Mapper les jetons sur les clés de site, pas sur l'index de position |
ERROR_WRONG_GOOGLEKEY |
Clé de site extraite de manière incorrecte | Vérifiez la clé de site à partir de l'attribut iframe src ou data-sitekey |
FAQ
Une page peut-elle avoir des clés de site différentes pour chaque CAPTCHA ?
Oui. Chaque widget peut utiliser une clé de site distincte. Extrayez toujours la clé de site par widget plutôt que de supposer qu'ils en partagent une.
Dois-je les résoudre séquentiellement ou en parallèle ?
En parallèle. Chaque résolution prend 15 à 30 secondes. Résoudre deux CAPTCHA en parallèle prend le même temps que d’en résoudre un seul.
Que se passe-t-il si le deuxième CAPTCHA n'apparaît qu'après avoir soumis le premier formulaire ?
Analysez à nouveau la page après chaque soumission de formulaire pour détecter les CAPTCHA nouvellement affichés. Utilisez WebDriverWait (Selenium) ou page.waitForSelector (Puppeteer) pour attendre le nouveau widget.
Résolvez n'importe quel nombre de CAPTCHA par page avec CaptchaAI
Obtenez votre clé API surcaptchaai.com.
Guides associés
- Extraction Iframe CAPTCHA : résolution des CAPTCHA dans des cadres imbriqués
- Extraction des paramètres reCAPTCHA de la source de la page
- Résolution parallèle de CAPTCHA