Anwendungsfälle

CAPTCHA-Scraping mit Python: Vollständige Anleitung

Ja – CAPTCHA-geschützte Seiten lassen sich in Python scrapen, ohne einen echten Browser zu starten. Für die meisten Formulare genügt die Kombination aus requests, BeautifulSoup und einem externen Solver, der die CAPTCHA-Abfrage in ein gültiges Token verwandelt. Genau dieses Token schickt Ihr Skript anschließend zusammen mit den übrigen Formulardaten ab – für die Zielseite sieht die Anfrage aus wie eine regulär gelöste Challenge.

Diese Anleitung baut Schritt für Schritt einen produktionsreifen Scraper auf, der reCAPTCHA v2/v3, Cloudflare Turnstile und klassische Bild-CAPTCHAs über die API von CaptchaAI löst. Der komplette Code kommt ohne Headless-Browser aus; erst wenn eine Seite ihre Inhalte per JavaScript nachlädt, führt der Weg über Selenium oder Playwright.

Warum requests statt Selenium?

Ein browser-freier Scraper hat gegenüber einer Selenium- oder Playwright-Lösung mehrere handfeste Vorteile:

  • Geschwindigkeit: reine HTTP-Anfragen sind um ein Vielfaches schneller als ein gerenderter Browser.
  • Ressourcen: kein Chromium-Prozess, kaum Speicherbedarf – ideal für viele parallele Worker.
  • Stabilität: keine Timing-Probleme durch DOM-Rendering oder Wartezeiten auf Elemente.
  • Kosten: mehr gleichzeitige Anfragen pro Server bedeuten weniger Infrastruktur.

Der Preis dafür: Sie müssen die Formularlogik selbst nachbauen. Setzt eine Seite ihre Inhalte ausschließlich per JavaScript zusammen, bleibt ein echter Browser die bessere Wahl.

Voraussetzungen

Voraussetzung Details
Python 3.7+ inklusive pip
requests pip install requests
beautifulsoup4 pip install beautifulsoup4
CaptchaAI API-Schlüssel kostenlos über captchaai.com

CaptchaAI rechnet Thread-basiert ab, nicht pro Lösung. Schon der Einstiegstarif BASIC (15 $/Monat, 5 Threads) enthält unbegrenzte Lösungen pro Thread – die Kosten pro gescrapter Seite steigen also nicht mit dem Volumen, sondern sind allein durch Ihre parallelen Threads begrenzt.

Die CaptchaAI-Solver-Klasse in Python

Kapseln Sie die API-Aufrufe in einer wiederverwendbaren Klasse. Sie folgt dem klassischen Zwei-Schritt-Muster: Das CAPTCHA wird an in.php übermittelt, danach fragt der Client res.php so lange ab, bis das Ergebnis vorliegt.

import requests
import time

class CaptchaSolver:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base = "https://ocr.captchaai.com"

    def _submit(self, params):
        params["key"] = self.api_key
        resp = requests.get(f"{self.base}/in.php", params=params)
        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit error: {resp.text}")
        return resp.text.split("|")[1]

    def _poll(self, task_id, timeout=300):
        deadline = time.time() + timeout
        while time.time() < deadline:
            time.sleep(5)
            resp = requests.get(f"{self.base}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id
            })
            if resp.text == "CAPCHA_NOT_READY":
                continue
            if resp.text.startswith("OK|"):
                return resp.text.split("|")[1]
            raise Exception(f"Solve error: {resp.text}")
        raise TimeoutError("Solve timed out")

    def solve_recaptcha_v2(self, site_key, page_url):
        task_id = self._submit({
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url
        })
        return self._poll(task_id)

    def solve_recaptcha_v3(self, site_key, page_url, action="verify"):
        task_id = self._submit({
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "version": "v3",
            "action": action
        })
        return self._poll(task_id)

    def solve_turnstile(self, site_key, page_url):
        task_id = self._submit({
            "method": "turnstile",
            "sitekey": site_key,
            "pageurl": page_url
        })
        return self._poll(task_id)

    def solve_image(self, image_base64):
        task_id = self._submit({
            "method": "base64",
            "body": image_base64
        })
        return self._poll(task_id)

_submit reicht die Parameter samt API-Schlüssel ein und liefert die Task-ID zurück; _poll fragt das Ergebnis alle 5 Sekunden ab, bis es vorliegt oder das Timeout von 300 Sekunden greift. Für jeden Typ gibt es eine eigene Methode – der Feldname des Tokens (g-recaptcha-response, cf-turnstile-response) richtet sich danach, welche Challenge die Seite einsetzt.

Ein reCAPTCHA-geschütztes Formular scrapen

Der typische Ablauf besteht aus fünf Schritten: Seite laden, Sitekey aus dem g-recaptcha-Div auslesen, CAPTCHA lösen, Token zusammen mit den Formulardaten absenden und schließlich die Antwort parsen.

from bs4 import BeautifulSoup
import requests

solver = CaptchaSolver("YOUR_API_KEY")
session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})

# Step 1: Load the page
url = "https://example.com/search"
page = session.get(url)
soup = BeautifulSoup(page.text, "html.parser")

# Step 2: Extract the site key
recaptcha_div = soup.find("div", class_="g-recaptcha")
site_key = recaptcha_div["data-sitekey"]

# Step 3: Solve the CAPTCHA
token = solver.solve_recaptcha_v2(site_key, url)

# Step 4: Submit the form with the token
form_data = {
    "q": "search term",
    "g-recaptcha-response": token
}
result = session.post(url, data=form_data)

# Step 5: Parse the results
result_soup = BeautifulSoup(result.text, "html.parser")
items = result_soup.find_all("div", class_="result-item")
for item in items:
    print(item.text.strip())

Entscheidend ist der vierte Schritt: Das Token gehört in dasselbe POST-Formular wie die übrigen Felder, bei reCAPTCHA immer unter dem Namen g-recaptcha-response.

Hinweis: reCAPTCHA-Tokens verfallen nach rund 120 Sekunden. Lösen Sie das CAPTCHA daher unmittelbar vor dem Absenden und niemals auf Vorrat.

Paginierte Ergebnisse hinter CAPTCHAs scrapen

Sitzt hinter jeder Ergebnisseite ein eigenes CAPTCHA, lösen Sie es pro Seite direkt in der Schleife. Eine kurze Pause zwischen den Anfragen hält den Scraper höflich und senkt das Risiko einer Sperre:

def scrape_all_pages(base_url, site_key, max_pages=10):
    solver = CaptchaSolver("YOUR_API_KEY")
    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    })
    all_results = []

    for page_num in range(1, max_pages + 1):
        page_url = f"{base_url}?page={page_num}"

        # Solve CAPTCHA for each page if needed
        token = solver.solve_recaptcha_v2(site_key, page_url)

        resp = session.get(page_url, params={
            "g-recaptcha-response": token,
            "page": page_num
        })

        soup = BeautifulSoup(resp.text, "html.parser")
        items = soup.find_all("div", class_="item")

        if not items:
            break

        all_results.extend([item.text.strip() for item in items])
        print(f"Page {page_num}: {len(items)} items")

        time.sleep(2)  # Polite delay

    return all_results

Die Schleife bricht ab, sobald eine Seite keine Ergebnisse mehr liefert. Bei sehr vielen Seiten lohnt es sich, die Tokens mehrerer Anfragen parallel zu lösen und die Threads Ihres Tarifs auszureizen.

Bild-CAPTCHAs verarbeiten

Ältere Portale setzen häufig noch klassische Text-im-Bild-CAPTCHAs ein. Hier laden Sie die Bilddatei herunter, kodieren sie als Base64 und schicken sie an den base64-Endpunkt der API:

import base64

def scrape_with_image_captcha(url):
    solver = CaptchaSolver("YOUR_API_KEY")
    session = requests.Session()

    page = session.get(url)
    soup = BeautifulSoup(page.text, "html.parser")

    # Find the CAPTCHA image
    captcha_img = soup.find("img", {"id": "captcha-image"})
    captcha_url = captcha_img["src"]

    # Download and encode the image
    img_resp = session.get(captcha_url)
    img_base64 = base64.b64encode(img_resp.content).decode()

    # Solve
    captcha_text = solver.solve_image(img_base64)

    # Submit
    form_data = {
        "captcha": captcha_text,
        "username": "user"
    }
    result = session.post(url, data=form_data)
    return result.text

Die zurückgegebene Zeichenkette tragen Sie als normales Formularfeld ein. Laden Sie das Bild in derselben Session herunter, in der Sie das Formular absenden – sonst passt das CAPTCHA nicht zur Sitzung.

Fehlerbehandlung und Wiederholungsversuche

Produktions-Scraper brauchen Wiederholungslogik. Der folgende Wrapper wiederholt einen fehlgeschlagenen Lösungsversuch mehrmals, bevor er die Ausnahme durchreicht:

def solve_with_retry(solver, site_key, page_url, max_retries=3):
    for attempt in range(max_retries):
        try:
            return solver.solve_recaptcha_v2(site_key, page_url)
        except Exception as e:
            if attempt == max_retries - 1:
                raise
            print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
            time.sleep(2)

Kombinieren Sie den Wrapper mit exponentiellem Backoff, damit kurzfristige Netzwerk- oder Auslastungsspitzen den Scraper nicht sofort abbrechen lassen. Erst nach mehreren erfolglosen Versuchen sollte die Ausnahme durchschlagen.

Fehlerbehebung: die häufigsten Fehlercodes

Problem Ursache Lösung
ERROR_WRONG_USER_KEY ungültiger API-Schlüssel Schlüssel im Dashboard prüfen
ERROR_ZERO_BALANCE kein Guthaben Konto aufladen
Nach dem Absenden erscheint erneut die CAPTCHA-Seite Token abgelaufen oder falscher Feldname Token sofort verwenden; Namen der Formularfelder prüfen
ConnectionError Netzwerkproblem Wiederholungslogik mit exponentiellem Backoff ergänzen
leere Ergebnisse nach der Übermittlung Seite benötigt Cookies/Session requests.Session() verwenden, um Cookies zu halten

Die meisten Produktionsfehler lassen sich auf drei Ursachen zurückführen: ein abgelaufenes Token, einen falschen Feldnamen oder eine fehlende Session mit Cookies. Prüfen Sie diese drei Punkte zuerst, bevor Sie tiefer suchen.

Scraping und DSGVO in der DACH-Region

Vor dem ersten Live-Lauf lohnt in Deutschland, Österreich und der Schweiz ein Blick auf die Rechtslage. IP-Adressen und personenbezogene Inhalte fallen unter die DSGVO – prüfen Sie Rechtsgrundlage und Zweckbindung Ihrer Datenflüsse und respektieren Sie die robots.txt der Zielseite. Für den Betrieb bieten sich europäische Hoster wie Hetzner oder netcup an, auf denen Sie Worker samt Proxy-Pool nah an den Zielservern betreiben. Technisch löst CaptchaAI die CAPTCHA-Abfrage; die Verantwortung für zulässiges und zweckgebundenes Scraping bleibt bei Ihnen.

Best Practices für den Dauerbetrieb

Für stabile Scraper im Produktivbetrieb haben sich einige Regeln bewährt:

  • Nutzen Sie durchgehend requests.Session(), damit Cookies und Header über alle Anfragen erhalten bleiben.
  • Rotieren Sie Proxys und User-Agents, um Sperren durch die Zielseite vorzubeugen.
  • Lösen Sie jedes Token unmittelbar vor der Übermittlung, nicht im Voraus.
  • Protokollieren Sie Fehlercodes, um Probleme wie ERROR_ZERO_BALANCE oder einen ungültigen Schlüssel früh zu erkennen.

Häufige Fragen

Wie finde ich den Sitekey einer reCAPTCHA-Seite?

Im HTML-Quelltext steht er im Attribut data-sitekey des g-recaptcha-Divs. Genau diesen Wert liest der Scraper oben per BeautifulSoup aus.

Wie scrape ich viele Seiten parallel?

Über asynchrone Anfragen. Mit aiohttp lösen Sie Dutzende CAPTCHAs gleichzeitig, begrenzt nur durch die Threads Ihres Tarifs. Details: aiohttp mit CaptchaAI asynchron nutzen.

Wie verhindere ich, dass mein Scraper blockiert wird?

Pausen zwischen Anfragen, rotierende Proxys und realistische Header. Mehr dazu: Proxy-Rotation beim CAPTCHA-Scraping.

Zahlt CaptchaAI pro gelöstem CAPTCHA extra?

Nein. Abgerechnet wird pro Thread, nicht pro Lösung – innerhalb Ihres Tarifs lösen Sie beliebig viele reCAPTCHA-, Turnstile- oder Bild-CAPTCHAs.

Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.