Fehlerbehebung

Browser-Automatisierungs-CAPTCHA schlägt fehl, aber die API funktioniert: Debug-Anleitung

Wenn das per API gelöste Token in einem requests-Skript sauber durchläuft, im automatisierten Browser aber abgelehnt wird, liegt das fast nie am Token selbst. Meist wurde der Callback nicht ausgelöst oder das Token war beim Absenden bereits abgelaufen. Diese Anleitung nimmt die sechs häufigsten Ursachen der Reihe nach auseinander – von der Callback-Auslösung über das Timing bis zur Bot-Erkennung jenseits des CAPTCHAs.

Ein typisches Muster aus DACH-Teams: Der API-Integrationstest in der GitLab-CI-Pipeline ist grün, doch im echten Selenium-Browser bleibt der Absenden-Button deaktiviert. Genau diese Lücke schließen die folgenden Abschnitte.

Die drei Ursachen, die in der Praxis am häufigsten dahinterstecken:

  • Callback nicht ausgelöst – das Token liegt im Feld, aber das Formular erfährt nie davon.
  • Token abgelaufen – zwischen Lösung und Absenden vergehen mehr als rund 120 Sekunden.
  • Falsche Page-URL – an die API ging eine andere URL als die, auf der der Browser tatsächlich steht.

Wo die Token-Übergabe typischerweise scheitert

Problem Symptom Grundursache
Token landet nicht im richtigen Element Formular wird ohne Token gesendet Falscher Textarea-Selektor
Token vor dem Absenden abgelaufen Seite zeigt erneut ein CAPTCHA Zu viel Zeit zwischen Lösen und Absenden
Callback nicht ausgelöst Absenden-Button bleibt deaktiviert Fehlender grecaptcha-Callback
Falsche Page-URL an die API übergeben Token für diese Domain ungültig URL stimmt nicht überein
Bot-Erkennung jenseits des CAPTCHAs Sperre, obwohl das Token akzeptiert wurde Browser-Fingerprinting

In welcher Reihenfolge Sie prüfen sollten

Arbeiten Sie die Ursachen nicht wahllos ab, sondern von der wahrscheinlichsten zur seltensten. In den meisten DACH-Support-Fällen steckt der Fehler bereits in den ersten beiden Punkten – das spart Ihnen den Umweg über aufwendige Browser-Analysen:

  1. Prüfen Sie, ob der Callback nach dem Einfügen des Tokens tatsächlich feuert – das ist mit Abstand die häufigste Ursache.
  2. Messen Sie die Zeitspanne zwischen Lösung und Absenden; ein reCAPTCHA-v2-Token verfällt nach rund 120 Sekunden.
  3. Vergleichen Sie die an die API übergebene Page-URL mit dem, was driver.current_url tatsächlich liefert.
  4. Kontrollieren Sie erst zuletzt eine nachgelagerte Bot-Erkennung – sie greift seltener, als in der Praxis vermutet wird.

Hinweis: Die Techniken in dieser Anleitung dienen der Fehlersuche in Ihren eigenen oder ausdrücklich autorisierten Automatisierungen. Testen Sie Token-Handling und Formular-Submits ausschließlich gegen Systeme, für die Sie die Berechtigung haben.


Schnelldiagnose nach Symptom

Ordnen Sie Ihr Symptom einer Ursache zu und springen Sie direkt zur passenden Lösung weiter unten:

Symptom Ursache Fix
Token gesetzt, Formular wird trotzdem abgelehnt Callback nicht ausgelöst Callback-Funktion suchen und aufrufen
Absenden-Button bleibt deaktiviert Erst der Callback aktiviert ihn Callback nach dem Einfügen des Tokens auslösen
„Ungültiges CAPTCHA" nach dem Absenden Token abgelaufen Zeit zwischen Lösung und Übermittlung verkürzen
403 nach dem Absenden des Formulars Bot-Erkennung (kein CAPTCHA) Browser-Flags und Proxy-Konfiguration prüfen
Läuft mit requests, scheitert im Browser Abweichende Page-URL driver.current_url als Page-URL verwenden

Lösung 1: reCAPTCHA-v2-Token korrekt einfügen und Callback auslösen

Das Token muss in die Textarea g-recaptcha-response eingetragen werden UND der Callback muss feuern – erst beides zusammen macht die Übermittlung gültig:

from selenium import webdriver
from selenium.webdriver.common.by import By
import time


def inject_recaptcha_token(driver, token):
    """Properly inject reCAPTCHA v2 token in browser."""

    # Step 1: Make the response textarea visible and set the value
    driver.execute_script("""
        // Find all response textareas (may be multiple on page)
        var textareas = document.querySelectorAll('[name="g-recaptcha-response"]');
        textareas.forEach(function(ta) {
            ta.style.display = 'block';
            ta.value = arguments[0];
        });

        // Also set via ID if present
        var byId = document.getElementById('g-recaptcha-response');
        if (byId) {
            byId.style.display = 'block';
            byId.value = arguments[0];
        }
    """, token)

    # Step 2: Trigger the callback
    driver.execute_script("""
        // Try standard callback
        if (typeof ___grecaptcha_cfg !== 'undefined') {
            var clients = ___grecaptcha_cfg.clients;
            for (var key in clients) {
                var client = clients[key];
                // Navigate nested structure to find callback
                for (var prop in client) {
                    var val = client[prop];
                    if (val && typeof val === 'object') {
                        for (var subprop in val) {
                            var subval = val[subprop];
                            if (subval && typeof subval === 'object' && subval.callback) {
                                subval.callback(arguments[0]);
                                return;
                            }
                        }
                    }
                }
            }
        }
        // Fallback: try common callback names
        if (typeof onCaptchaSuccess === 'function') onCaptchaSuccess(arguments[0]);
        else if (typeof captchaCallback === 'function') captchaCallback(arguments[0]);
    """, token)


# Usage
driver = webdriver.Chrome()
driver.get("https://example.com/form")
# ... solve CAPTCHA via CaptchaAI ...
inject_recaptcha_token(driver, token)
time.sleep(1)
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

Der häufigste Fehler hier: Das Token wird gesetzt, aber Schritt 2 fehlt – ohne ausgelösten Callback bleibt die Seite in ihrem „ungelöst"-Zustand.


Lösung 2: Formulare mit Callback-Abhängigkeit freischalten

Manche Formulare bleiben deaktiviert, bis der CAPTCHA-Callback feuert – das Setzen des Tokens allein reicht dann nicht:

def find_and_trigger_callback(driver, token):
    """Find the reCAPTCHA callback and trigger it."""

    # Method 1: Extract callback from data attribute
    callback_name = driver.execute_script("""
        var widget = document.querySelector('.g-recaptcha');
        if (widget) {
            return widget.getAttribute('data-callback');
        }
        return null;
    """)

    if callback_name:
        driver.execute_script(f"window['{callback_name}'](arguments[0]);", token)
        return True

    # Method 2: Extract from grecaptcha config
    triggered = driver.execute_script("""
        try {
            var widgetId = 0;
            var callback = ___grecaptcha_cfg.clients[widgetId].aa.l.callback;
            if (typeof callback === 'function') {
                callback(arguments[0]);
                return true;
            }
        } catch(e) {}
        return false;
    """, token)

    return triggered

Lösung 3: Timing – Token sofort nach dem Lösen einsetzen

Ein reCAPTCHA-v2-Token ist nur rund 120 Sekunden gültig. Wer zwischen Lösung und Absenden zu lange wartet, sendet ein bereits abgelaufenes Token. Fragen Sie das Ergebnis daher zügig ab und fügen Sie es unmittelbar ein:

import requests
import time


def solve_and_inject_fast(driver, api_key, sitekey, page_url):
    """Solve CAPTCHA and inject token with minimal delay."""
    # Submit solve request
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "json": 1,
    }, timeout=30)
    task_id = resp.json()["request"]

    # Poll for result
    time.sleep(15)
    for _ in range(24):
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key, "action": "get",
            "id": task_id, "json": 1,
        }, timeout=15)
        data = resp.json()

        if data.get("status") == 1:
            token = data["request"]

            # IMMEDIATELY inject — don't wait
            inject_recaptcha_token(driver, token)

            # Submit form within 5 seconds
            time.sleep(0.5)
            return True

        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("Solve timeout")

Lösung 4: Die richtige Page-URL bei SPAs und Iframes

Bei Single-Page-Apps und iframe-gebundenen Widgets weicht die tatsächlich geladene URL oft von der ursprünglich angesteuerten ab. Übergeben Sie an die API die URL, die der Browser gerade wirklich anzeigt – nicht die, zu der Sie navigiert haben:

def get_correct_pageurl(driver):
    """Get the URL that reCAPTCHA actually sees."""

    # Check if CAPTCHA is in an iframe
    iframes = driver.find_elements(By.TAG_NAME, "iframe")

    for iframe in iframes:
        src = iframe.get_attribute("src") or ""
        if "recaptcha" in src or "anchor" in src:
            # CAPTCHA is in iframe — use parent page URL
            return driver.current_url

    # For SPAs, use current URL (may differ from initial load)
    return driver.current_url


# WRONG — using the URL you navigated to
pageurl = "https://example.com/form"  # May have redirected

# CORRECT — using the URL the browser is actually on
pageurl = get_correct_pageurl(driver)

Lösung 5: Wenn die Website trotz gültigem Token blockiert

Manchmal ist das Token einwandfrei, und die Seite lehnt die Anfrage trotzdem ab. Dann greift keine CAPTCHA-Prüfung mehr, sondern eine nachgelagerte Bot-Erkennung, die auf Automatisierungsmerkmale des Browsers reagiert. Ein realistisch konfigurierter Browser mit stabilen Startparametern reduziert diese Fehlalarme:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service


def create_browser_driver():
    """Create a browser that avoids basic bot detection."""
    options = Options()

    # Remove automation indicators
    options.add_argument("--disable-blink-features=AutomationControlled")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option("useAutomationExtension", False)

    # Set realistic window size
    options.add_argument("--window-size=1920,1080")

    driver = webdriver.Chrome(options=options)

    return driver


driver = create_browser_driver()

Zur Einordnung: CaptchaAI löst die CAPTCHA-Abfrage. Ob eine nachgelagerte Prüfung Ihre Anfrage akzeptiert, hängt vom Umfeld ab – testen Sie ausschließlich in eigenen oder autorisierten Umgebungen.


Lösung 6: Cloudflare-Turnstile-Token einfügen

Cloudflare Turnstile nutzt ein eigenes Antwortfeld (cf-turnstile-response) und erwartet zusätzlich ein ausgelöstes change-Ereignis:

def inject_turnstile_token(driver, token):
    """Inject Turnstile token into the page."""
    driver.execute_script("""
        // Find Turnstile response input
        var inputs = document.querySelectorAll(
            'input[name="cf-turnstile-response"], ' +
            'input[name="g-recaptcha-response"]'
        );

        inputs.forEach(function(input) {
            input.value = arguments[0];
        });

        // Trigger change event
        inputs.forEach(function(input) {
            input.dispatchEvent(new Event('change', { bubbles: true }));
            input.dispatchEvent(new Event('input', { bubbles: true }));
        });

        // Try Turnstile callback
        if (window.turnstile) {
            var widgets = document.querySelectorAll('[data-callback]');
            widgets.forEach(function(w) {
                var cb = w.getAttribute('data-callback');
                if (typeof window[cb] === 'function') {
                    window[cb](arguments[0]);
                }
            });
        }
    """, token)

Debug-Checkliste: Seitenzustand ausgeben, bevor Sie raten

Wenn keiner der Fixes greift, lassen Sie sich den Zustand der Seite ausgeben – ob die Textarea existiert, ein Wert gesetzt ist, grecaptcha geladen wurde und welcher Callback registriert ist:

def debug_captcha_injection(driver, token):
    """Print debug info to diagnose injection failures."""
    info = driver.execute_script("""
        var result = {};

        // Check textarea exists
        var ta = document.querySelector('[name="g-recaptcha-response"]');
        result.textarea_found = !!ta;
        result.textarea_value_set = ta ? ta.value.length > 0 : false;

        // Check reCAPTCHA loaded
        result.grecaptcha_loaded = typeof grecaptcha !== 'undefined';

        // Check for callback
        var widget = document.querySelector('.g-recaptcha');
        result.has_data_callback = widget ? !!widget.getAttribute('data-callback') : false;
        result.callback_name = widget ? widget.getAttribute('data-callback') : null;

        // Current URL
        result.current_url = window.location.href;

        // Check for errors in console (basic)
        result.has_submit_button = !!document.querySelector('button[type=submit], input[type=submit]');

        return result;
    """)

    for key, value in info.items():
        print(f"  {key}: {value}")

    return info

Häufige Fragen

Warum bleibt der Absenden-Button trotz gültigem Token deaktiviert?

Weil der Callback fehlt. Viele Formulare aktivieren den Button erst, wenn die Callback-Funktion aufgerufen wurde – das reine Setzen von g-recaptcha-response genügt nicht. Lösen Sie den Callback nach dem Einfügen des Tokens explizit aus (Lösung 1 und 2).

Wie schnell muss ich ein reCAPTCHA-v2-Token nach dem Lösen einsetzen?

Innerhalb von rund 120 Sekunden. Danach läuft es ab, und die Seite fordert ein neues CAPTCHA an. Fügen Sie das Token direkt nach der Ergebnisabfrage ein und senden Sie das Formular unmittelbar danach.

Wie finde ich den Namen der Callback-Funktion auf einer Seite?

Über das Attribut data-callback des .g-recaptcha-Elements. Ist es leer, lässt sich der Callback über die interne ___grecaptcha_cfg-Struktur auslesen – beide Wege zeigt der Code in Lösung 2.

Warum kommt nach dem Absenden ein 403, obwohl das CAPTCHA akzeptiert wurde?

Dann greift eine Bot-Erkennung unabhängig vom CAPTCHA. Prüfen Sie die Browser-Startparameter und Ihre Proxy-Konfiguration und testen Sie ausschließlich in eigenen oder autorisierten Umgebungen.

Gilt derselbe Ansatz auch für Playwright oder Puppeteer?

Ja. Das Prinzip bleibt identisch: Token in g-recaptcha-response eintragen, danach den Callback auslösen. Nur die Methode zum Ausführen von JavaScript unterscheidet sich – in Playwright page.evaluate(), in Puppeteer page.evaluate() statt driver.execute_script().


Verwandte Leitfäden


Weiterführende Ressourcen

Kommentare sind für diesen Artikel deaktiviert.