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_BALANCEoder 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.