Tutorials

reCAPTCHA in Single-Page-Anwendungen: Dynamische Lademuster

Einzelseitenanwendungen laden reCAPTCHA dynamisch – das Widget existiert nicht im ursprünglichen HTML. Ein React-Anmeldeformular rendert das CAPTCHA nur, wenn die Komponente gemountet wird. Eine Vue-Checkout-Seite lädt reCAPTCHA, nachdem der Benutzer auf „Bestellung aufgeben“ klickt. Beim traditionellen Page-Source-Scraping werden diese vollständig übersehen. Hier erfahren Sie, wie Sie dynamisch geladene reCAPTCHAs erkennen und lösen.

Warum SPAs anders sind

Traditionelle Seite SPA
reCAPTCHA-Skript im anfänglichen HTML Skript nach Routenänderung eingefügt
Widget wird beim Laden der Seite gerendert Widget wird beim Komponentenmount gerendert
Site-Schlüssel in der Seitenquelle Site-Schlüssel im JavaScript-Bundle
Das Formular wird per POST übermittelt Formular wird über XHR/fetch gesendet

In einem SPA ist das reCAPTCHA-Widget möglicherweise erst vorhanden, wenn eine bestimmte Benutzeraktion es auslöst. Ihre Automatisierung muss warten, bis das Widget angezeigt wird.

Erkennen des reCAPTCHA-Widgets

Warten Sie auf das Element

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("https://example.com/login")

    # SPA may need a click or navigation to trigger reCAPTCHA
    page.click("#show-login-form")

    # Wait for reCAPTCHA iframe to appear
    recaptcha_frame = page.wait_for_selector(
        "iframe[src*='recaptcha']",
        timeout=15000
    )
    print("reCAPTCHA detected:", recaptcha_frame.get_attribute("src"))

Extrahieren Sie den Site-Schlüssel

Der Site-Schlüssel kann sich an mehreren Stellen in einem SPA befinden:

# Method 1: From the iframe src
iframe_src = recaptcha_frame.get_attribute("src")
# src contains: ...?k=6LcR_RsTAAAAADge...
import re
match = re.search(r'[?&]k=([^&]+)', iframe_src)
site_key = match.group(1) if match else None

# Method 2: From data-sitekey attribute
site_key = page.eval_on_selector(
    "[data-sitekey]",
    "el => el.getAttribute('data-sitekey')"
)

# Method 3: From JavaScript bundle (last resort)
site_key = page.evaluate("""
    () => {
        // Check for grecaptcha render calls
        const scripts = document.querySelectorAll('script');
        for (const s of scripts) {
            const match = s.textContent.match(/sitekey['":\s]+(['"])(6L[^'"]+)\\1/);
            if (match) return match[2];
        }
        return null;
    }
""")

Lösung des reCAPTCHA

Sobald Sie den Site-Schlüssel haben, senden Sie ihn an CaptchaAI:

import requests
import time

def solve_recaptcha(site_key, page_url):
    # Submit task
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_API_KEY",
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1
    })
    task_id = resp.json()["request"]

    # Poll for result
    for _ in range(60):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]
    raise TimeoutError("Solve timed out")

Injizieren des Tokens in ein SPA

SPAs reichen keine herkömmlichen Formulare ein. Sie müssen das Token dort einfügen, wo die Anwendung es erwartet.

Methode 1: Legen Sie den ausgeblendeten Textbereich fest

token = solve_recaptcha(site_key, "https://example.com/login")

# Inject into the g-recaptcha-response textarea
page.evaluate(f"""
    document.getElementById('g-recaptcha-response').value = '{token}';
""")

# Submit the form
page.click("#login-button")

Methode 2: Callback auslösen

Viele SPA-reCAPTCHA-Implementierungen verwenden eine Callbackfunktion:

# Find the callback function name
callback = page.evaluate("""
    () => {
        const widget = document.querySelector('.g-recaptcha');
        return widget?.getAttribute('data-callback') || null;
    }
""")

# Call it with the token
if callback:
    page.evaluate(f"window['{callback}']('{token}')")
else:
    # Try the default grecaptcha callback
    page.evaluate(f"""
        if (window.grecaptcha) {{
            // Set the response and trigger verification
            document.getElementById('g-recaptcha-response').value = '{token}';
            // Find and call any registered callbacks
            const form = document.querySelector('form');
            if (form) form.dispatchEvent(new Event('submit'));
        }}
    """)

Methode 3: XHR abfangen (JavaScript / Puppeteer)

const puppeteer = require('puppeteer');

async function solveRecaptchaInSPA() {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  // Intercept the API call and inject the token
  await page.setRequestInterception(true);
  page.on('request', request => {
    if (request.url().includes('/api/login')) {
      const postData = JSON.parse(request.postData() || '{}');
      // Token already set via page.evaluate — let it pass
      request.continue();
    } else {
      request.continue();
    }
  });

  await page.goto('https://example.com/login');
  await page.waitForSelector('[data-sitekey]');

  const siteKey = await page.$eval(
    '[data-sitekey]',
    el => el.getAttribute('data-sitekey')
  );

  // Mit CaptchaAI lösen (Implementierung ausgelassen, entspricht Python)
  const token = await solveCaptcha(siteKey, page.url());

  // Inject token and trigger callback
  await page.evaluate((t) => {
    document.getElementById('g-recaptcha-response').value = t;
    const callback = document.querySelector('.g-recaptcha')
      ?.getAttribute('data-callback');
    if (callback && window[callback]) {
      window[callback](t);
    }
  }, token);
}

Framework-spezifische Muster

Reagieren (react-google-recaptcha)

React-Apps verwenden häufig das Paket react-google-recaptcha. Die Komponente rendert asynchron:

# Wait for React to mount the component
page.wait_for_selector(".g-recaptcha", state="attached")

# The sitekey is in the rendered div's data attribute
site_key = page.eval_on_selector(
    ".g-recaptcha", "el => el.dataset.sitekey"
)

Vue (vue-recaptcha)

# Vue may use v-if to conditionally render
# Navigate or interact to trigger the condition
page.click("#proceed-to-checkout")
page.wait_for_selector("iframe[src*='recaptcha']")

Eckig

# Angular apps may lazy-load reCAPTCHA modules
# Wait for the specific Angular component
page.wait_for_selector("re-captcha, app-recaptcha, [data-sitekey]")

Umgang mit Routenänderungen

SPAs verwenden die Seite wieder – die Navigation erfolgt ohne vollständiges Neuladen:

# Listen for reCAPTCHA appearing after SPA navigation
page.goto("https://example.com")

# Navigate within the SPA
page.click("a[href='/login']")

# The URL changed but no page reload happened
# Wait for the CAPTCHA to render in the new "page"
page.wait_for_selector("iframe[src*='recaptcha']", timeout=10000)

Fehlerbehebung

Problem Ursache Lösung
Token wird erzeugt, aber vom Ziel abgelehnt sitekey, pageurl oder Session-Kontext stimmen nicht Erfasse Parameter erneut und verwende den Token in derselben Browser- oder HTTP-Sitzung
Polling endet im Timeout Intervall, Wartezeit oder Fehlerbehandlung sind zu eng gesetzt Poll alle 5-10 Sekunden, trenne Timeout von echten Fehlercodes und logge die Ursache
Beispiel funktioniert lokal, aber nicht im Workflow Callback, Form-Feld oder Token-Injektion fehlt in der echten Zielkette Prüfe den exakten Übergabepfad vom Solver bis zur finalen Zielanfrage

Verwandte Leitfäden

  • reCAPTCHA v2 Callback-Mechanismus
  • reCAPTCHA v2 Unsichtbar: Triggererkennung
  • Shadow DOM CAPTCHA-Verwaltung

Diskussionen (0)

Noch keine Kommentare.