Tutorials

CAPTCHA in Pop-up-Modalen: Erkennung und Token-Injektion

Ein Benutzer klickt auf „Senden“ und ein Modal erscheint mit einer CAPTCHA-Aufforderung. Der Sitekey befindet sich nicht in der ursprünglichen Seitenquelle – er wird dynamisch geladen, wenn das Modal geöffnet wird. Ihr Automatisierungsskript muss das Modal auslösen, auf das Rendern des CAPTCHA warten, Parameter extrahieren, lösen und das Token einfügen, bevor das Modal abläuft oder die Benutzersitzung abläuft.

Modale CAPTCHA-Muster

Muster Auslöser Herausforderung
Anmeldemodalität Klicken Sie auf die Schaltfläche „Anmelden“. CAPTCHA wird innerhalb des Overlay-Divs geladen
Anti-Bot-Interstitial Automatisch nach verdächtigem Verhalten Seite mit modalen Blöcken im Vollbildmodus
Checkout-Bestätigung Zahlungsformular absenden Modal wird zur Überprüfung angezeigt
Dialog zur Ratenbegrenzung Zu viele Anfragen erkannt Modal mit CAPTCHA-Gate
Cookie-Einwilligung + CAPTCHA Erster Besuch CAPTCHA im Einwilligungsdialog eingebettet

Python: Playwright Modaler CAPTCHA-Handler

import requests
import time
from playwright.sync_api import sync_playwright

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


def solve_captcha(sitekey, pageurl, method="userrecaptcha"):
    """Submit and poll a CAPTCHA."""
    params = {
        "key": API_KEY,
        "method": method,
        "json": 1,
    }
    if method == "userrecaptcha":
        params["googlekey"] = sitekey
        params["pageurl"] = pageurl
    elif method == "turnstile":
        params["sitekey"] = sitekey
        params["pageurl"] = 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 detect_modal_captcha(page):
    """
    Detect CAPTCHA inside a visible modal/dialog.
    Returns (sitekey, method) or (None, None).
    """
    return page.evaluate("""
        () => {
            // Find visible modals
            const modalSelectors = [
                '.modal.show',
                '.modal[style*="display: block"]',
                'dialog[open]',
                '[role="dialog"]:not([aria-hidden="true"])',
                '.overlay.visible',
                '.popup.active',
                '[class*="modal"][class*="open"]',
            ];

            let modal = null;
            for (const sel of modalSelectors) {
                const el = document.querySelector(sel);
                if (el && el.offsetParent !== null) {
                    modal = el;
                    break;
                }
            }

            // If no modal found, search entire document
            const searchRoot = modal || document;

            // Check for reCAPTCHA
            const recaptcha = searchRoot.querySelector('.g-recaptcha[data-sitekey]');
            if (recaptcha) {
                return { sitekey: recaptcha.dataset.sitekey, method: 'userrecaptcha' };
            }

            // Check for Turnstile
            const turnstile = searchRoot.querySelector('.cf-turnstile[data-sitekey]');
            if (turnstile) {
                return { sitekey: turnstile.dataset.sitekey, method: 'turnstile' };
            }

            // Check for hCaptcha
            const hcaptcha = searchRoot.querySelector('.h-captcha[data-sitekey]');
            if (hcaptcha) {
                return { sitekey: hcaptcha.dataset.sitekey, method: 'hcaptcha' };
            }

            return null;
        }
    """)


def inject_token_in_modal(page, token, method="userrecaptcha"):
    """Inject token into the CAPTCHA inside the modal."""
    if method == "userrecaptcha":
        page.evaluate("""
            (token) => {
                // Find response textarea (may be inside modal)
                const textareas = document.querySelectorAll('#g-recaptcha-response, [name="g-recaptcha-response"]');
                textareas.forEach(ta => {
                    ta.value = token;
                    ta.style.display = 'block';
                });

                // Trigger callback
                if (typeof ___grecaptcha_cfg !== 'undefined') {
                    Object.values(___grecaptcha_cfg.clients).forEach(client => {
                        Object.values(client).forEach(val => {
                            if (val && typeof val === 'object') {
                                Object.values(val).forEach(v => {
                                    if (v && typeof v.callback === 'function') v.callback(token);
                                });
                            }
                        });
                    });
                }
            }
        """, token)
    elif method == "turnstile":
        page.evaluate("""
            (token) => {
                const inputs = document.querySelectorAll('[name="cf-turnstile-response"]');
                inputs.forEach(inp => { inp.value = token; });

                if (typeof window.turnstileCallback === 'function') {
                    window.turnstileCallback(token);
                }
            }
        """, token)


def handle_modal_captcha(page, trigger_selector=None, timeout=10000):
    """
    Full workflow: trigger modal, detect CAPTCHA, solve, inject.
    """
    # Step 1: Trigger the modal if needed
    if trigger_selector:
        print(f"Clicking trigger: {trigger_selector}")
        page.click(trigger_selector)

    # Step 2: Wait for modal to become visible
    print("Waiting for modal...")
    modal_selectors = [
        ".modal.show",
        "dialog[open]",
        '[role="dialog"]:not([aria-hidden="true"])',
        ".popup.active",
    ]

    modal_visible = False
    for selector in modal_selectors:
        try:
            page.wait_for_selector(selector, timeout=timeout)
            modal_visible = True
            print(f"  Modal detected: {selector}")
            break
        except Exception:
            continue

    if not modal_visible:
        print("  No modal detected")
        return None

    # Step 3: Wait for CAPTCHA to render inside modal
    time.sleep(2)  # Brief pause for dynamic CAPTCHA loading

    captcha_info = detect_modal_captcha(page)
    if not captcha_info:
        print("  No CAPTCHA found in modal")
        return None

    sitekey = captcha_info["sitekey"]
    method = captcha_info["method"]
    print(f"  Found {method} CAPTCHA: {sitekey[:20]}...")

    # Step 4: Solve via CaptchaAI
    token = solve_captcha(sitekey, page.url, method)
    print(f"  Solved: {token[:30]}...")

    # Step 5: Inject token
    inject_token_in_modal(page, token, method)
    print("  Token injected")

    return token


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

        # Handle CAPTCHA that appears in login modal
        token = handle_modal_captcha(
            page,
            trigger_selector="button#login-btn",
            timeout=10000,
        )

        if token:
            # Fill form fields inside modal
            page.fill('dialog input[name="email"]', "user@example.com")
            page.fill('dialog input[name="password"]', "password123")

            # Submit modal form
            page.click('dialog button[type="submit"]')
            page.wait_for_load_state("networkidle")

        browser.close()


main()

JavaScript: Puppeteer Modal Handler

const puppeteer = require("puppeteer");

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

async function solveCaptcha(sitekey, pageurl, method = "userrecaptcha") {
  const body = new URLSearchParams({ key: API_KEY, method, json: "1" });
  if (method === "userrecaptcha") { body.set("googlekey", sitekey); body.set("pageurl", pageurl); }
  else if (method === "turnstile") { body.set("sitekey", sitekey); body.set("pageurl", 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 waitForModal(page, timeout = 10000) {
  const selectors = [".modal.show", "dialog[open]", '[role="dialog"]', ".popup.active"];
  for (const sel of selectors) {
    try {
      await page.waitForSelector(sel, { visible: true, timeout });
      return sel;
    } catch {}
  }
  return null;
}

async function detectModalCaptcha(page) {
  return page.evaluate(() => {
    const checks = [
      { sel: ".g-recaptcha[data-sitekey]", method: "userrecaptcha" },
      { sel: ".cf-turnstile[data-sitekey]", method: "turnstile" },
    ];
    for (const { sel, method } of checks) {
      const el = document.querySelector(sel);
      if (el && el.dataset.sitekey) return { sitekey: el.dataset.sitekey, method };
    }
    return null;
  });
}

async function handleModalCaptcha(page, triggerSelector) {
  // Trigger modal
  if (triggerSelector) await page.click(triggerSelector);

  // Wait for modal
  const modalSel = await waitForModal(page);
  if (!modalSel) { console.log("No modal found"); return null; }
  console.log(`Modal visible: ${modalSel}`);

  // Wait for CAPTCHA render
  await new Promise((r) => setTimeout(r, 2000));

  const info = await detectModalCaptcha(page);
  if (!info) { console.log("No CAPTCHA in modal"); return null; }

  console.log(`Found ${info.method}: ${info.sitekey.substring(0, 20)}...`);
  const token = await solveCaptcha(info.sitekey, page.url(), info.method);
  console.log(`Solved: ${token.substring(0, 30)}...`);

  // Inject token
  await page.evaluate((t, method) => {
    if (method === "userrecaptcha") {
      document.querySelectorAll("#g-recaptcha-response").forEach((el) => { el.value = t; });
    } else if (method === "turnstile") {
      document.querySelectorAll('[name="cf-turnstile-response"]').forEach((el) => { el.value = t; });
    }
  }, token, info.method);

  return token;
}

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto("https://example.com", { waitUntil: "networkidle2" });

  const token = await handleModalCaptcha(page, "button#login-btn");
  if (token) {
    await page.type('dialog input[name="email"]', "user@example.com");
    await page.click('dialog button[type="submit"]');
    await page.waitForNavigation();
  }

  await browser.close();
})();

Überlegungen zum modalen Timing

Faktor Auswirkungen Schadensbegrenzung
Zeitüberschreitung beim modalen automatischen Schließen Modal wird möglicherweise geschlossen, bevor die Lösung abgeschlossen ist Beginnen Sie sofort mit der Lösung, wenn Sie sie entdecken
Sitzungsablauf während der Lösung Die Serversitzung läuft im modalen Warten ab Halten Sie die Sitzung mit Hintergrund-Heartbeat am Leben
CAPTCHA-Renderverzögerung im Modalmodus Das Laden des Widgets im Modalmodus dauert 1–3 Sekunden Warten Sie 2 Sekunden, nachdem das Modal sichtbar ist, bevor Sie den Sitekey extrahieren
Ablauf des Tokens beim Ausfüllen des Formulars Das Token läuft beim Ausfüllen des modalen Formulars ab Lösen Sie das CAPTCHA zuletzt, nachdem Sie die anderen Felder ausgefüllt haben

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

Kommentare sind für diesen Artikel deaktiviert.

Verwandte Beiträge

Comparisons Headless vs. Headed Chrome für CAPTCHA-Tests in eigener QA
Wann sich Headless-Chrome und wann Headed-Chrome für CAPTCHA-Tests in eigenen CI- und QA-Pipelines eignet, und welche Auswirkungen die Wahl auf Stabilität und L...

Wann sich Headless-Chrome und wann Headed-Chrome für CAPTCHA-Tests in eigenen CI- und QA-Pipelines eignet, und...

Apr 17, 2026
Comparisons WebDriver vs. Chrome DevTools Protocol in eigener CAPTCHA-QA
Vergleich von Web Driver und Chrome Dev Tools Protocol für QA-Tests gegen die eigene CAPTCHA-Integration: Stärken, Grenzen und Einsatzempfehlungen.

Vergleich von Web Driver und Chrome Dev Tools Protocol für QA-Tests gegen die eigene CAPTCHA-Integration: Stär...

Apr 17, 2026