Tutorials

Iframe-CAPTCHA-Extraktion: Lösen von CAPTCHAs in verschachtelten Frames

Ein reCAPTCHA in einem verschachtelten Iframe lösen Sie mit denselben API-Aufrufen wie ein CAPTCHA auf der Hauptseite – der einzige Unterschied liegt darin, den Sitekey aus dem richtigen Frame zu lesen und das Token in exakt diesen Frame zurückzuschreiben. Wer stattdessen die URL der obersten Seite verwendet oder den Sitekey aus dem falschen Iframe zieht, bekommt ERROR_WRONG_GOOGLEKEY oder ein vom Server abgelehntes Token. Dieser Leitfaden zeigt den vollständigen Weg durch den Iframe-Baum – mit Selenium für explizites Frame-Switching und mit Puppeteer für dessen automatisches Frame-Handling.

Die Iframe-Struktur verstehen

Zahlungsformulare, Login-Widgets und modale Dialoge betten ihr CAPTCHA häufig in mehrere ineinander verschachtelte Iframes ein. Ein typischer Aufbau:

Main page
  └── iframe#payment-frame (cross-origin)
        └── iframe[src*="recaptcha/api2/anchor"] (Google-hosted)
              └── reCAPTCHA widget

Drei Punkte entscheiden darüber, ob Ihre Extraktion funktioniert:

  • Äußerer Iframe: Er rendert das Widget und trägt den Sitekey – im data-sitekey-Attribut oder als k=-Parameter im src des Anchor-Iframes.
  • Innerer Google-Iframe: Er zeigt nur die visuelle CAPTCHA-Abfrage; mit ihm interagieren Sie nie direkt.
  • Cross-Origin-Grenze: Die Attribute des inneren Iframes bleiben vom übergeordneten Frame aus lesbar, auch wenn der Iframe cross-origin ist.

Python: mit Selenium in den richtigen Frame wechseln

Mit Selenium wechseln Sie den Frame-Kontext explizit. Die Reihenfolge ist entscheidend:

  1. In den äußeren Iframe wechseln.
  2. Den Sitekey aus dem reCAPTCHA-Anchor-Iframe extrahieren.
  3. Über CaptchaAI lösen und das Ergebnis abfragen.
  4. Das Token in das textarea-Feld des Frames einfügen.
  5. Zum Hauptdokument zurückkehren.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import requests
import re
import time

API_KEY = "YOUR_API_KEY"
driver = webdriver.Chrome()
driver.get("https://example.com/checkout")

# Step 1: Switch into the outer iframe
wait = WebDriverWait(driver, 15)
outer_iframe = wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "iframe#payment-frame"))
)
driver.switch_to.frame(outer_iframe)

# Step 2: Find the reCAPTCHA iframe and extract sitekey
recaptcha_iframe = wait.until(
    EC.presence_of_element_located(
        (By.CSS_SELECTOR, 'iframe[src*="recaptcha/api2/anchor"]')
    )
)
src = recaptcha_iframe.get_attribute("src")
sitekey = re.search(r"k=([A-Za-z0-9_-]+)", src).group(1)
page_url = driver.current_url
print(f"Sitekey: {sitekey}")
print(f"Page URL: {page_url}")

# Schritt 3: Mit CaptchaAI lösen
resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url,
    "json": "1"
})
task_id = resp.json()["request"]

token = None
for _ in range(24):
    time.sleep(5)
    poll = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()
    if poll["status"] == 1:
        token = poll["request"]
        break
    if poll["request"] != "CAPCHA_NOT_READY":
        raise Exception(poll["request"])

print(f"Token: {token[:50]}...")

# Step 4: Inject token (still inside the outer iframe)
driver.execute_script("""
    document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];
    if (typeof grecaptcha !== 'undefined') {
        grecaptcha.getResponse = function() { return arguments[0]; };
    }
""", token)

# Step 5: Switch back to main page
driver.switch_to.default_content()
print("Token injected, switched back to main frame")

Erwartete Ausgabe:

Sitekey: 6Le-SITEKEY-abc123
Page URL: https://example.com/checkout
Token: 03AGdBq26ZfPxL...
Token injected, switched back to main frame

Die Schleife fragt das Ergebnis bis zu 24-mal im Abstand von 5 Sekunden ab. Lösen Sie Tokens erst unmittelbar vor dem Absenden – sie bleiben ohnehin nur rund 120 Sekunden gültig.

JavaScript: Frame-Handling mit Puppeteer

Puppeteer bringt vollwertige Frame-Unterstützung mit – ein manuelles Umschalten wie bei Selenium entfällt. Über contentFrame() erhalten Sie ein Handle auf den jeweiligen Frame und arbeiten darin, ohne den globalen Kontext zu wechseln.

const puppeteer = require('puppeteer');
const axios = require('axios');

const API_KEY = 'YOUR_API_KEY';

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

  // Step 1: Find the payment iframe
  const paymentFrameHandle = await page.waitForSelector('iframe#payment-frame');
  const paymentFrame = await paymentFrameHandle.contentFrame();

  // Step 2: Find reCAPTCHA iframe inside the payment frame
  const recaptchaFrameHandle = await paymentFrame.waitForSelector(
    'iframe[src*="recaptcha/api2/anchor"]'
  );
  const src = await paymentFrame.evaluate(
    el => el.getAttribute('src'), recaptchaFrameHandle
  );

  const sitekey = src.match(/k=([A-Za-z0-9_-]+)/)[1];
  const pageUrl = page.url();
  console.log(`Sitekey: ${sitekey}`);

  // Schritt 3: Mit CaptchaAI lösen
  const submitResp = await axios.post('https://ocr.captchaai.com/in.php', null, {
    params: {
      key: API_KEY,
      method: 'userrecaptcha',
      googlekey: sitekey,
      pageurl: pageUrl,
      json: 1
    }
  });
  const taskId = submitResp.data.request;

  let token = null;
  for (let i = 0; i < 24; i++) {
    await new Promise(r => setTimeout(r, 5000));
    const pollResp = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: 1 }
    });
    if (pollResp.data.status === 1) {
      token = pollResp.data.request;
      break;
    }
    if (pollResp.data.request !== 'CAPCHA_NOT_READY') {
      throw new Error(pollResp.data.request);
    }
  }

  console.log(`Token: ${token.substring(0, 50)}...`);

  // Step 4: Inject token into the payment frame
  await paymentFrame.evaluate((tkn) => {
    const textarea = document.querySelector(
      'textarea[name="g-recaptcha-response"]'
    );
    textarea.value = tkn;

    const callback = document.querySelector('.g-recaptcha')
      ?.getAttribute('data-callback');
    if (callback && typeof window[callback] === 'function') {
      window[callback](tkn);
    }
  }, token);

  console.log('Token injected into payment iframe');
})();

Der Ablauf ist derselbe wie in Selenium, nur ohne Kontextwechsel: paymentFrame.evaluate() führt das Einfügen direkt im richtigen Frame aus. Prüfen Sie zusätzlich das data-callback-Attribut des Widgets – viele Formulare senden erst dann ab, wenn diese Callback-Funktion mit dem Token aufgerufen wurde. Das reine Setzen des Textarea-Werts genügt in solchen Fällen nicht.

Drei oder mehr Frame-Ebenen

Bei drei oder mehr Ebenen verketten Sie die Frame-Wechsel weiter. Wichtig ist nur, den Rückweg zu kennen:

  • switch_to.parent_frame() bringt Sie eine Ebene nach oben.
  • switch_to.default_content() bringt Sie zurück zur Hauptseite.

Selenium

# Main → iframe-1 → iframe-2 → CAPTCHA
driver.switch_to.frame(driver.find_element(By.ID, "iframe-1"))
driver.switch_to.frame(driver.find_element(By.ID, "iframe-2"))
# Now extract sitekey from this context

# To go back up one level:
driver.switch_to.parent_frame()

# To go back to main:
driver.switch_to.default_content()

Puppeteer

const frame1 = await (await page.$('iframe#iframe-1')).contentFrame();
const frame2 = await (await frame1.$('iframe#iframe-2')).contentFrame();
// Extract sitekey from frame2

Praxisbeispiel: Zahlungs-Iframe im DACH-Checkout

In vielen deutschsprachigen Shops – etwa auf Basis von Shopware oder JTL – lädt die Checkout-Seite den Zahlungsschritt in einem eigenen Iframe des Payment-Providers. Sitzt dort ein reCAPTCHA, ist es doppelt verschachtelt: Hauptseite → Zahlungs-Iframe → reCAPTCHA-Anchor-Iframe. Genau dieser Fall ist im Selenium-Beispiel oben abgebildet (iframe#payment-frame); für QA-Tests gegen Ihre Staging-Umgebung passen Sie nur den Selektor an.

Zwei Punkte sollten Sie dabei berücksichtigen:

  • DSGVO: Beim Scraping mit echten Proxys gelten IP-Adressen als personenbezogene Daten. Prüfen Sie Rechtsgrundlage und Datenflüsse, bevor Sie gegen fremde Systeme statt gegen die eigene Staging-Umgebung arbeiten.
  • Cloudflare Turnstile: Dieselbe Frame-Navigation funktioniert auch hier – das Widget rendert ebenfalls in einem eigenen Iframe. Sie extrahieren dort den Sitekey und übermitteln ihn mit der Methode turnstile an CaptchaAI.

Typische Fehler und ihre Ursachen

Fehler Symptom Lösung
URL der Hauptseite als pageurl gesendet Token wird von der Website abgelehnt Die URL des Frames verwenden, der das CAPTCHA-Widget rendert
Nicht zurück zum übergeordneten Frame gewechselt Alle folgenden Aktionen schlagen fehl Nach dem Einfügen switch_to.parent_frame() bzw. switch_to.default_content() aufrufen
Sitekey aus dem falschen Iframe gelesen ERROR_WRONG_GOOGLEKEY Der Sitekey sitzt auf dem Iframe mit .g-recaptcha, nicht auf dem inneren Google-Challenge-Iframe
JavaScript im Cross-Origin-Iframe ausgeführt SecurityError in der Konsole execute_script funktioniert dort nicht – Sitekey stattdessen aus dem src-Attribut lesen

Häufige Fragen

Welche URL gehört bei verschachtelten Frames in pageurl?

Die URL der Seite, die das reCAPTCHA-Widget lädt – meist die eigene URL des Iframes, nicht die der obersten Seite. Im Zweifel prüfen Sie die reCAPTCHA-Konfiguration der Site, um den erwarteten Ursprung zu bestätigen.

Warum erscheint ERROR_WRONG_GOOGLEKEY, obwohl der Sitekey stimmt?

Meist stammt der Sitekey aus dem falschen Frame. Lesen Sie ihn immer aus dem Iframe mit dem .g-recaptcha-Element, nicht aus dem inneren Google-Challenge-Iframe. Auch eine nicht passende pageurl löst diesen Fehler aus.

Lässt sich dieselbe Technik für Cloudflare Turnstile nutzen?

Ja. Turnstile rendert sein Widget ebenfalls in einem eigenen Iframe. Sie navigieren identisch durch den Frame-Baum, extrahieren den Sitekey und übermitteln ihn mit der Methode turnstile an CaptchaAI.

Muss ich nach dem Einfügen des Tokens zum Hauptframe zurückwechseln?

In Selenium ja – rufen Sie switch_to.default_content() auf, bevor Sie das Formular absenden, sonst laufen die nächsten Befehle im Iframe-Kontext ins Leere. In Puppeteer arbeiten Sie über Frame-Handles und brauchen keinen expliziten Rückweg.

Wie finde ich heraus, wie tief das CAPTCHA verschachtelt ist?

Öffnen Sie die DevTools und wählen Sie das reCAPTCHA-Widget aus. Der Elementbaum zeigt jedes umgebende iframe-Element bis zur Hauptseite. Zählen Sie die Iframes zwischen Widget und document und bauen Sie genau so viele Frame-Wechsel in Ihr Skript ein. Cross-origin-Grenzen erkennen Sie an abweichenden src-Domains.

Iframe-CAPTCHAs zuverlässig lösen – mit CaptchaAI

Holen Sie sich Ihren API-Schlüssel auf captchaai.com und lösen Sie Ihr erstes Iframe-CAPTCHA in wenigen Minuten.

Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.