Anwendungsfälle

Scraping von CAPTCHA-geschützten Websites

Ein CAPTCHA muss Ihren Scraper nicht stoppen. Der zuverlässige Weg durch eine geschützte Seite folgt immer demselben Muster: den CAPTCHA-Typ erkennen, das Token über eine API lösen, das Token zurück in die Anfrage schreiben und den Abruf fortsetzen. Dieser Leitfaden zeigt drei praxiserprobte Strategien dafür – mit lauffähigem Python-Code für reCAPTCHA v2, Cloudflare Turnstile und ganzseitige Cloudflare-Blockaden – und wo die Grenzen der CaptchaAI-Unterstützung liegen.

Welche CAPTCHAs Ihnen beim Scraping begegnen

Bevor Sie eine Strategie wählen, sollten Sie wissen, welcher Typ auf der Zielseite steckt – davon hängt die passende CaptchaAI-Methode ab:

CAPTCHA Wo es auftritt CaptchaAI-Methode
reCAPTCHA v2 Anmelde- und Suchformulare method=userrecaptcha
reCAPTCHA v3 Hintergrund-Scoring auf jeder Seite method=userrecaptcha&version=v3
Cloudflare Turnstile Seiten hinter Cloudflare method=turnstile
Cloudflare Challenge Ganzseitige Cloudflare-Blockade method=cloudflare_challenge
Bild-/OCR-CAPTCHA Ältere Seiten, Produktkataloge method=base64

Wichtig: hCaptcha und FunCaptcha (Arkose Labs) werden von CaptchaAI derzeit nicht unterstützt; GeeTest v4 ist als „bald verfügbar" angekündigt, aber noch nicht verfügbar. Trifft Ihr Scraper auf diese Typen, planen Sie einen alternativen Ansatz ein statt eines API-Aufrufs. Unterstützt sind reCAPTCHA v2/v3 (inklusive Enterprise), Cloudflare Turnstile und Challenge, GeeTest v3 sowie Bild-, OCR- und Raster-CAPTCHAs.

Strategie 1: CAPTCHA erst bei Bedarf lösen

Der robusteste Ansatz ist reaktiv: Sie scrapen ganz normal und lösen ein CAPTCHA nur dann, wenn die Antwort tatsächlich eines enthält. Die folgende Scraper-Klasse erkennt reCAPTCHA v2 und Cloudflare Turnstile im HTML, holt das Token über CaptchaAI und sendet die Anfrage erneut ab:

import requests
import time
from bs4 import BeautifulSoup

API_KEY = "YOUR_API_KEY"

class ProtectedScraper:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })

    def scrape(self, url):
        resp = self.session.get(url)

        # Check for CAPTCHA
        if self._has_captcha(resp.text):
            resp = self._handle_captcha(resp.text, url)

        return resp.text

    def _has_captcha(self, html):
        indicators = ["g-recaptcha", "cf-turnstile", "h-captcha", "captcha"]
        return any(ind in html.lower() for ind in indicators)

    def _handle_captcha(self, html, url):
        soup = BeautifulSoup(html, "html.parser")

        # reCAPTCHA v2
        rc = soup.find("div", class_="g-recaptcha")
        if rc:
            token = self._solve_recaptcha(rc["data-sitekey"], url)
            return self.session.post(url, data={"g-recaptcha-response": token})

        # Cloudflare Turnstile
        ts = soup.find("div", class_="cf-turnstile")
        if ts:
            token = self._solve_turnstile(ts["data-sitekey"], url)
            return self.session.post(url, data={"cf-turnstile-response": token})

        raise Exception("Unknown CAPTCHA type")

    def _solve_recaptcha(self, site_key, page_url):
        resp = requests.get("https://ocr.captchaai.com/in.php", params={
            "key": API_KEY, "method": "userrecaptcha",
            "googlekey": site_key, "pageurl": page_url
        })
        return self._poll(resp.text.split("|")[1])

    def _solve_turnstile(self, site_key, page_url):
        resp = requests.get("https://ocr.captchaai.com/in.php", params={
            "key": API_KEY, "method": "turnstile",
            "sitekey": site_key, "pageurl": page_url
        })
        return self._poll(resp.text.split("|")[1])

    def _poll(self, task_id):
        for _ in range(60):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": API_KEY, "action": "get", "id": task_id
            })
            if result.text == "CAPCHA_NOT_READY": continue
            if result.text.startswith("OK|"): return result.text.split("|")[1]
            raise Exception(result.text)
        raise TimeoutError()

# Usage
scraper = ProtectedScraper()
html = scraper.scrape("https://example.com/data")

Das _poll-Verfahren fragt den Status im Abstand von fünf Sekunden ab, bis das Ergebnis vorliegt oder das Timeout greift – so blockieren Sie keinen Thread länger als nötig.

Strategie 2: Bekannte CAPTCHA-Seiten vorab lösen

Wenn Sie wissen, dass eine bestimmte Seite immer ein CAPTCHA verlangt – etwa ein geschütztes Suchformular –, sparen Sie einen Roundtrip: Lösen Sie vorab und senden Sie das Token direkt mit der Anfrage.

def scrape_known_captcha_page(url, site_key):
    # Solve before even loading the page
    token = solve_recaptcha(site_key, url)

    # Submit directly with token
    resp = requests.post(url, data={
        "g-recaptcha-response": token,
        "query": "search term"
    })
    return resp.text

Lösen Sie das Token allerdings erst unmittelbar vor dem Absenden – reCAPTCHA- und Turnstile-Tokens sind nur rund 120 Sekunden gültig und lassen sich nicht auf Vorrat halten.

Strategie 3: Cloudflare-geschützte Seiten und cf_clearance

Seiten hinter Cloudflare verlangen häufig ein gültiges cf_clearance-Cookie, bevor sie Inhalte ausliefern. Lösen Sie die Cloudflare Challenge über denselben Proxy, den Sie später für die Abrufe nutzen, und übernehmen Sie Cookie und User-Agent aus der Antwort:

def get_cloudflare_clearance(url, proxy):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "cloudflare_challenge",
        "pageurl": url,
        "proxy": proxy,
        "proxytype": "HTTP"
    })
    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY": continue
        if "cf_clearance" in result.text:
            # Parse cf_clearance and user_agent from response
            return result.text
    raise TimeoutError()

Damit die Freigabe gültig bleibt, müssen alle nachfolgenden Anfragen dieselbe Proxy-IP und denselben User-Agent verwenden wie bei der Lösung – sonst verwirft Cloudflare das Cookie.

Muster für das Scraping mehrerer Seiten

Für seitenweises Scraping kapseln Sie den Ablauf in einer Schleife, fangen Fehler pro Seite ab und legen zufällige Pausen zwischen den Anfragen ein, damit das Zugriffsmuster nicht zu regelmäßig wirkt:

def scrape_multiple_pages(base_url, site_key, pages):
    scraper = ProtectedScraper()
    results = []

    for page in pages:
        url = f"{base_url}?page={page}"
        try:
            html = scraper.scrape(url)
            soup = BeautifulSoup(html, "html.parser")
            items = soup.find_all("div", class_="item")
            results.extend([item.text.strip() for item in items])
            print(f"Page {page}: {len(items)} items")
        except Exception as e:
            print(f"Page {page} failed: {e}")

        time.sleep(random.uniform(2, 5))

    return results

Häufige Probleme und Lösungen

Problem Lösung
CAPTCHA erscheint bei jeder Anfrage Proxys einsetzen und die Anfragerate senken
Token wird nach dem Lösen abgelehnt Token ist wahrscheinlich abgelaufen – innerhalb von 120 Sekunden verwenden
Cloudflare blockiert trotz cf_clearance Für alle Anfragen denselben Proxy und User-Agent verwenden
Seite liefert nach dem Lösen eine andere Seite Auf zusätzliche Weiterleitungen oder Cookies prüfen

Rechtlicher Kontext und Sorgfaltspflicht

Für Leser im DACH-Raum lohnt ein kurzer Blick auf die Rahmenbedingungen, bevor ein Scraper produktiv läuft. Das Erheben öffentlich zugänglicher Daten ist rechtlich etwas anderes als das Auslesen von Inhalten hinter einer Anmeldung oder einer Zugangsbeschränkung. In der EU gelten IP-Adressen als personenbezogene Daten (DSGVO); prüfen Sie deshalb Rechtsgrundlage, die AGB der Zielseite und Ihre Datenflüsse. Für Worker und Proxys bieten sich Anbieter mit europäischer Infrastruktur wie Hetzner, IONOS oder netcup an – das erleichtert die Argumentation gegenüber der eigenen Compliance und hält Latenzen niedrig.

Häufige Fragen

Wann löst ein Scraper überhaupt ein CAPTCHA aus?

Meist erst dann, wenn das Anfrageverhalten auffällt: zu viele Anfragen pro Minute, fehlende Cookies oder ein verdächtiger User-Agent. Verteilen Sie Anfragen über Proxys, halten Sie realistische Pausen ein und lösen Sie das CAPTCHA nur, wenn es tatsächlich erscheint.

Welcher CaptchaAI-Plan passt zu großvolumigem Scraping?

CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung – jeder Plan enthält unbegrenzte Lösungen pro Thread. Wählen Sie den Plan nach der Zahl paralleler Abfragen: BASIC (15 $/Monat, 5 Threads) für erste Tests, ADVANCE (90 $/Monat, 50 Threads) oder PREMIUM (170 $/Monat, 100 Threads) für dauerhaft hohe Parallelität. Preise in US-Dollar.

Ist das Scraping CAPTCHA-geschützter Seiten rechtlich zulässig?

Das hängt vom Einzelfall ab. Öffentliche Daten zu erheben ist etwas anderes als das Auslesen geschützter, zugangsbeschränkter Inhalte. Prüfen Sie in der EU insbesondere die DSGVO, die Nutzungsbedingungen der Zielseite und Ihre eigene Rechtsgrundlage, bevor Sie einen Scraper dauerhaft betreiben.

Wie gehe ich mit stark JavaScript-lastigen Seiten um?

Rendern Sie die Seite mit Selenium, Puppeteer oder Playwright, lesen Sie dann Sitekey und Page-URL aus dem DOM und lösen Sie das CAPTCHA über CaptchaAI. Anschließend schreiben Sie das Token in das Formularfeld und setzen den Ablauf fort. Details in Selenium: CAPTCHAs in Python lösen.

Warum wird mein Token nach dem Lösen abgelehnt?

Weil es abgelaufen ist. reCAPTCHA- und Turnstile-Tokens sind nur rund 120 Sekunden gültig. Lösen Sie das CAPTCHA erst unmittelbar vor dem Absenden und übermitteln Sie das Token sofort – nicht vorab lösen und zwischenspeichern.

Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.