Résolution de Problèmes

Gestion de reCAPTCHA v2 et Cloudflare Turnstile sur le même site

Certains sites utilisent reCAPTCHA sur leur page de connexion et Turnstile sur leur page de paiement. Autres tests A/B entre fournisseurs — la même URL affiche reCAPTCHA pour un visiteur et Turnstile pour un autre. Le codage en dur d'une seule méthode de solveur signifie que votre automatisation s'arrête chaque fois qu'elle rencontre l'autre type.

Pourquoi les sites utilisent plusieurs fournisseurs CAPTCHA

Scénario Comment cela apparaît
Différentes pages, différents fournisseurs Connexion = reCAPTCHA, paiement = Tourniquet
Fournisseurs de tests A/B La même page affiche aléatoirement l'un ou l'autre type
Migration en cours Les anciennes pages ont reCAPTCHA, les nouvelles pages ont Turnstile
Repli en cas d'échec Le fournisseur principal échoue. → revient au fournisseur secondaire.
Variation régionale reCAPTCHA pour les visiteurs américains, Turnstile pour l'UE (GDPR)

Python : détection et résolution automatiques

import requests
import time
import re
from dataclasses import dataclass

API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


@dataclass
class CaptchaInfo:
    provider: str     # "recaptcha" or "turnstile"
    method: str       # API method name
    sitekey: str
    pageurl: str
    response_field: str  # Form field name for the token


def detect_captcha_type(html, pageurl):
    """
    Detect which CAPTCHA provider is on the page.
    Returns CaptchaInfo or None.
    """
    # Check for Turnstile
    turnstile_match = re.search(
        r'class=["\'][^"\']*cf-turnstile[^"\']*["\'][^>]*data-sitekey=["\']([^"\']+)["\']',
        html,
    )
    if not turnstile_match:
        turnstile_match = re.search(
            r'data-sitekey=["\']([^"\']+)["\'][^>]*class=["\'][^"\']*cf-turnstile',
            html,
        )

    if turnstile_match:
        return CaptchaInfo(
            provider="turnstile",
            method="turnstile",
            sitekey=turnstile_match.group(1),
            pageurl=pageurl,
            response_field="cf-turnstile-response",
        )

    # Check for reCAPTCHA
    recaptcha_match = re.search(
        r'class=["\'][^"\']*g-recaptcha[^"\']*["\'][^>]*data-sitekey=["\']([^"\']+)["\']',
        html,
    )
    if not recaptcha_match:
        recaptcha_match = re.search(
            r'data-sitekey=["\']([^"\']+)["\'][^>]*class=["\'][^"\']*g-recaptcha',
            html,
        )

    # Also check for script-rendered reCAPTCHA
    if not recaptcha_match:
        recaptcha_match = re.search(
            r'grecaptcha\.render\([^,]+,\s*\{[^}]*["\']sitekey["\']\s*:\s*["\']([^"\']+)["\']',
            html,
        )

    if recaptcha_match:
        return CaptchaInfo(
            provider="recaptcha",
            method="userrecaptcha",
            sitekey=recaptcha_match.group(1),
            pageurl=pageurl,
            response_field="g-recaptcha-response",
        )

    return None


def solve_captcha(info):
    """Solve any detected CAPTCHA type via CaptchaAI."""
    params = {
        "key": API_KEY,
        "method": info.method,
        "json": 1,
    }

    if info.method == "userrecaptcha":
        params["googlekey"] = info.sitekey
        params["pageurl"] = info.pageurl
    elif info.method == "turnstile":
        params["sitekey"] = info.sitekey
        params["pageurl"] = info.pageurl

    resp = requests.post(SUBMIT_URL, data=params, timeout=30).json()
    if resp.get("status") != 1:
        raise RuntimeError(f"Submit failed: {resp.get('request')}")

    task_id = resp["request"]
    for _ in range(60):
        time.sleep(5)
        poll = requests.get(RESULT_URL, params={
            "key": API_KEY, "action": "get",
            "id": task_id, "json": 1,
        }, timeout=15).json()

        if poll.get("request") == "CAPCHA_NOT_READY":
            continue
        if poll.get("status") == 1:
            return poll["request"]
        raise RuntimeError(f"Solve failed: {poll.get('request')}")

    raise RuntimeError("Timeout")


def process_page(session, url):
    """Fetch page, detect CAPTCHA type, solve, and return form-ready data."""
    response = session.get(url)
    captcha_info = detect_captcha_type(response.text, url)

    if not captcha_info:
        print(f"No CAPTCHA detected on {url}")
        return None

    print(f"Detected {captcha_info.provider} on {url}")
    print(f"  Sitekey: {captcha_info.sitekey[:30]}...")

    token = solve_captcha(captcha_info)
    print(f"  Solved: {token[:30]}...")

    return {
        "provider": captcha_info.provider,
        "response_field": captcha_info.response_field,
        "token": token,
    }


# Usage: Handle multiple pages with different providers
session = requests.Session()

pages = [
    "https://example.com/login",      # Might have reCAPTCHA
    "https://example.com/checkout",   # Might have Turnstile
]

for url in pages:
    result = process_page(session, url)
    if result:
        form_data = {result["response_field"]: result["token"]}
        # Add other form fields...
        # session.post(url, data=form_data)

JavaScript : détection dynamique de CAPTCHA

const API_KEY = "YOUR_API_KEY";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";

function detectCaptchaType(html, pageurl) {
  // Turnstile
  const turnstileMatch = html.match(/cf-turnstile[^>]*data-sitekey=["']([^"']+)["']/);
  if (turnstileMatch) {
    return { provider: "turnstile", method: "turnstile", sitekey: turnstileMatch[1], pageurl, field: "cf-turnstile-response" };
  }

  // reCAPTCHA
  const recaptchaMatch = html.match(/g-recaptcha[^>]*data-sitekey=["']([^"']+)["']/);
  if (recaptchaMatch) {
    return { provider: "recaptcha", method: "userrecaptcha", sitekey: recaptchaMatch[1], pageurl, field: "g-recaptcha-response" };
  }

  // Script-rendered reCAPTCHA
  const scriptMatch = html.match(/sitekey["']\s*:\s*["']([^"']+)["']/);
  if (scriptMatch) {
    return { provider: "recaptcha", method: "userrecaptcha", sitekey: scriptMatch[1], pageurl, field: "g-recaptcha-response" };
  }

  return null;
}

async function solveCaptcha(info) {
  const body = new URLSearchParams({ key: API_KEY, method: info.method, json: "1" });
  if (info.method === "userrecaptcha") { body.set("googlekey", info.sitekey); body.set("pageurl", info.pageurl); }
  else if (info.method === "turnstile") { body.set("sitekey", info.sitekey); body.set("pageurl", info.pageurl); }

  const resp = await (await fetch(SUBMIT_URL, { method: "POST", body })).json();
  if (resp.status !== 1) throw new Error(`Submit: ${resp.request}`);

  const taskId = resp.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const url = `${RESULT_URL}?key=${API_KEY}&action=get&id=${taskId}&json=1`;
    const poll = await (await fetch(url)).json();
    if (poll.request === "CAPCHA_NOT_READY") continue;
    if (poll.status === 1) return poll.request;
    throw new Error(`Solve: ${poll.request}`);
  }
  throw new Error("Timeout");
}

async function processPage(url) {
  const response = await fetch(url);
  const html = await response.text();
  const info = detectCaptchaType(html, url);

  if (!info) { console.log(`No CAPTCHA on ${url}`); return null; }
  console.log(`${info.provider} detected on ${url}`);

  const token = await solveCaptcha(info);
  return { provider: info.provider, field: info.field, token };
}

// Usage
const pages = ["https://example.com/login", "https://example.com/checkout"];
for (const url of pages) {
  const result = await processPage(url);
  if (result) {
    console.log(`Solved ${result.provider}: ${result.token.substring(0, 30)}...`);
  }
}

Référence de détection des fournisseurs

Fournisseur Marqueur HTML URL du script Champ de réponse
reCAPTCHA v2 class="g-recaptcha" google.com/recaptcha/api.js g-recaptcha-response
Cloudflare Turnstile class="cf-turnstile" challenges.cloudflare.com/turnstile cf-turnstile-response
hCaptcha class="h-captcha" js.hcaptcha.com/1/api.js h-captcha-response

Dépannage

Problème Parce que Corriger
Mauvais type de CAPTCHA détecté Regex correspond au mauvais élément Vérifiez d'abord les noms de classe spécifiques au fournisseur, pas seulement data-sitekey
Jeton rejeté après une résolution correcte Nom de champ de réponse incorrect utilisé Faire correspondre le nom du champ au fournisseur : g-recaptcha-response vs cf-turnstile-response
Le type CAPTCHA change entre les visites Test A/B ou sélection géo-basée Détectez toujours de manière dynamique ; ne jamais coder en dur le fournisseur
Les deux fournisseurs détectés sur une seule page L'un d'entre eux peut être caché/inactive Vérifiez la visibilité des éléments : résolvez uniquement le CAPTCHA visible
La détection échoue pour les CAPTCHA rendus par script Aucun marqueur HTML dans la source Recherchez les appels grecaptcha.render() ou turnstile.render() dans les scripts

FAQ

Une page peut-elle utiliser à la fois reCAPTCHA et Turnstile simultanément ?

Rarement sur le même formulaire, mais cela se produit dans différentes parties d'un site. Si les deux apparaissent sur une seule page, généralement un seul est actif – vérifiez quel widget est visible et possède un data-sitekey non vide.

CaptchaAI utilise-t-il la même API pour les deux fournisseurs ?

Les mêmes points de terminaison (in.php / res.php) mais des valeurs method différentes. reCAPTCHA utilise method=userrecaptcha avec googlekey, tandis que Turnstile utilise method=turnstile avec sitekey. Le modèle de détection automatique gère ce mappage.

Comment gérer le basculement du fournisseur ?

Si un site passe de reCAPTCHA à Turnstile en cours de session (par exemple, après l'échec du chargement de reCAPTCHA), détectez à nouveau le type de CAPTCHA sur chaque demande de page plutôt que de mettre en cache le fournisseur dès la première visite.

Articles connexes

Prochaines étapes

Gérer les sites avec plusieurs fournisseurs CAPTCHA –récupérez votre clé API CaptchaAIet mettre en œuvre la détection automatique.

Guides associés :

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