Fehlerbehebung

Umgang mit reCAPTCHA v2 und Cloudflare Turnstile auf derselben Site

Einige Websites verwenden reCAPTCHA auf ihrer Anmeldeseite und Turnstile auf ihrer Checkout-Seite. Andere A/B-Tests zwischen Anbietern – die gleiche URL zeigt reCAPTCHA für einen Besucher und Turnstile für einen anderen. Die Festcodierung einer einzelnen Solver-Methode führt dazu, dass Ihre Automatisierung immer dann unterbrochen wird, wenn sie auf den anderen Typ trifft.

Warum Websites mehrere CAPTCHA-Anbieter verwenden

Szenario Wie es erscheint
Unterschiedliche Seiten, unterschiedliche Anbieter Login = reCAPTCHA, Kasse = Turnstile
A/B-Testanbieter Auf derselben Seite werden zufällig beide Typen angezeigt
Migration läuft Alte Seiten haben reCAPTCHA, neue Seiten haben Turnstile
Rückfall bei Scheitern Der primäre Anbieter schlägt fehl. → greift auf den sekundären Anbieter zurück
Regionale Variation reCAPTCHA für US-Besucher, Turnstile für EU (DSGVO)

Python: Automatisch erkennen und lösen

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: Dynamische CAPTCHA-Erkennung

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)}...`);
  }
}

Referenz zur Anbietererkennung

Anbieter HTML-Marker Skript-URL Antwortfeld
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

Fehlerbehebung

Problem Ursache Lösung
Ergebnis passt nicht zum eigenen Fall Solver-Typ oder Eingabeparameter wurden falsch auf den Zieltyp gemappt Vergleiche Zielseite, Solver-Methode und Pflichtparameter noch einmal systematisch
Beispiel läuft, aber Produktion scheitert Session, Header oder Proxy-Kontext weichen vom Test ab Übertrage erfolgreiche Testbedingungen möglichst unverändert in den Live-Workflow
Fehler bleiben unklar Logs enthalten zu wenig Kontext für eine belastbare Diagnose Protokolliere Solver-Typ, Latenz, Fehlercode und Downstream-Reaktion gemeinsam

Verwandte Leitfäden

  • reCAPTCHA v2-Callback-Mechanismus
  • So lösen Sie Cloudflare Turnstile per API
  • reCAPTCHA v2 vs. Invisible reCAPTCHA

Diskussionen (0)

Noch keine Kommentare.