Wer Flug-, Hotel- und Mietwagenpreise dauerhaft im Blick behalten will, stößt früher oder später auf reCAPTCHA, Cloudflare Turnstile und Bild-CAPTCHAs – genau an der Stelle, an der ein automatisierter Preis-Check sonst abbricht. CaptchaAI löst diese Abfragen per API und gibt das Token zurück, sodass Ihr Monitor die Preisseite abrufen und weiterverarbeiten kann. Dieser Leitfaden zeigt einen lauffähigen Fare-Monitor in Python, die passende Zeitplanung per Cron und eine realistische Kostenrechnung – ohne Umwege über fragile Browser-Tricks.
Welche CAPTCHAs auf Reiseportalen warten
Reiseseiten gehören zu den am aggressivsten geschützten Zielen im Web: Der Preis ist die Ware, und jede automatisierte Abfrage kostet die Anbieter Rechenzeit und Wettbewerbsvorteil. Entsprechend gestaffelt ist die Abwehr. Ein direkter Airline-Auftritt setzt oft auf reCAPTCHA v3 im Hintergrund, während große OTAs zusätzlich Cloudflare Turnstile vor kritische Endpunkte schalten. Wer weiß, welcher Typ wo auftaucht, plant den Monitor gezielter.
| Seitentyp | CAPTCHA-Typ | Schwierigkeit |
|---|---|---|
| Fluggesellschaften (direkt) | reCAPTCHA v3, Cloudflare | Mittel |
| OTAs (Expedia, Booking) | reCAPTCHA v2, Cloudflare Turnstile | Mittel–Hoch |
| Metasuche (Google Flights, Kayak) | reCAPTCHA v3 | Mittel |
| Billigflieger | Bild-CAPTCHA, reCAPTCHA | Niedrig–Mittel |
| Hotelaggregatoren | Cloudflare Challenge | Hoch |
Wie CaptchaAI in den Monitor passt
Der Ablauf ist bei jedem Typ identisch: Ihr Skript übermittelt Sitekey und Seiten-URL an den Endpunkt in.php, fragt das Ergebnis über res.php ab und trägt das zurückgelieferte Token in die Anfrage ein. reCAPTCHA v2 und v3, Cloudflare Turnstile und Challenge sowie klassische Bild-CAPTCHAs unterstützt CaptchaAI regulär – also genau die vier Familien, die auf Reiseportalen dominieren. hCaptcha wird nicht unterstützt, spielt in diesem Segment aber ohnehin kaum eine Rolle.
Abgerechnet wird pro gleichzeitigem Thread, nicht pro Lösung – jedes Paket enthält unbegrenzte Lösungen pro Thread im Abrechnungsmonat. Für die Preisüberwachung heißt das: Sie zahlen für parallele Kapazität, nicht für jede einzelne Abfrage. Das BASIC-Paket (15 $/Monat, 5 Threads) reicht für persönliche Watchlists; wer Hunderte Routen im Minutentakt prüft, greift zu ADVANCE (90 $/Monat, 50 Threads) oder höher. Ein Timeout von rund fünf Minuten pro Abfrage ist im Skript großzügig bemessen, damit auch langsamere Challenges sicher durchlaufen.
Fare-Monitor in Python
Das folgende Beispiel kapselt den Lösungsvorgang in solve_captcha und baut darum eine FareMonitor-Klasse, die Preisseiten abruft, reCAPTCHA und Turnstile erkennt und die extrahierten Preise samt Zeitstempel in einer Historie ablegt. Die Preisextraktion arbeitet bewusst mit einem robusten Muster und einem Plausibilitätsfilter (20 $ bis 10.000 $), um Rauschen wie Rabattbeträge oder Fußnoten auszusortieren.
import requests
import time
import re
import json
import os
from datetime import datetime, timedelta
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
def solve_captcha(params):
params["key"] = API_KEY
resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
if not resp.text.startswith("OK|"):
raise Exception(f"Submit: {resp.text}")
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 result.text.startswith("OK|"):
return result.text.split("|", 1)[1]
raise Exception(f"Solve: {result.text}")
raise TimeoutError()
class FareMonitor:
def __init__(self):
self.session = requests.Session()
self.session.headers["User-Agent"] = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0"
)
self.history = []
def fetch_with_captcha(self, url):
"""Fetch a travel page, solving CAPTCHAs if encountered."""
resp = self.session.get(url)
# reCAPTCHA v2/v3
match = re.search(
r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', resp.text
)
if match:
site_key = match.group(1)
# Detect v3 vs v2
if "recaptcha/api.js?render=" in resp.text:
token = solve_captcha({
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": url,
"version": "v3",
"action": "search",
})
else:
token = solve_captcha({
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": url,
})
resp = self.session.post(url, data={
"g-recaptcha-response": token,
})
# Cloudflare Turnstile
if "cf-turnstile" in resp.text:
match = re.search(
r'data-sitekey=["\']([^"\']+)', resp.text
)
if match:
token = solve_captcha({
"method": "turnstile",
"sitekey": match.group(1),
"pageurl": url,
})
resp = self.session.post(url, data={
"cf-turnstile-response": token,
})
return resp.text
def check_fares(self, routes):
"""Check fares for a list of routes."""
results = []
for route in routes:
try:
html = self.fetch_with_captcha(route["url"])
prices = self._extract_prices(html)
result = {
"route": f"{route['origin']}-{route['destination']}",
"date": route["date"],
"prices": prices,
"min_price": min(prices) if prices else None,
"timestamp": datetime.utcnow().isoformat(),
}
results.append(result)
self.history.append(result)
if prices:
print(f" {result['route']} ({route['date']}): "
f"${min(prices)}-${max(prices)}")
else:
print(f" {result['route']}: No prices found")
time.sleep(3) # Respectful delay
except Exception as e:
print(f" {route.get('origin', '?')}-"
f"{route.get('destination', '?')}: ERROR - {e}")
return results
def _extract_prices(self, html):
"""Extract prices from travel page HTML."""
prices = []
# Common price patterns
for match in re.finditer(
r'\$\s*([\d,]+(?:\.\d{2})?)', html
):
price = float(match.group(1).replace(",", ""))
if 20 < price < 10000: # Filter noise
prices.append(price)
return sorted(set(prices))
def detect_price_drops(self, threshold_pct=5):
"""Detect significant price drops in history."""
route_prices = {}
for entry in self.history:
key = f"{entry['route']}_{entry['date']}"
if key not in route_prices:
route_prices[key] = []
if entry["min_price"]:
route_prices[key].append(entry["min_price"])
alerts = []
for key, prices in route_prices.items():
if len(prices) >= 2:
prev = prices[-2]
current = prices[-1]
change_pct = ((current - prev) / prev) * 100
if change_pct < -threshold_pct:
alerts.append({
"route": key,
"previous": prev,
"current": current,
"change": f"{change_pct:.1f}%",
})
return alerts
def export_report(self, filename="fare_report.json"):
"""Export fare history to JSON."""
with open(filename, "w") as f:
json.dump(self.history, f, indent=2)
print(f"Exported {len(self.history)} fare checks to {filename}")
# Define routes to monitor
routes = [
{
"origin": "JFK",
"destination": "LAX",
"date": "2025-03-15",
"url": "https://example-airline.com/flights?from=JFK&to=LAX&date=2025-03-15",
},
{
"origin": "SFO",
"destination": "ORD",
"date": "2025-03-20",
"url": "https://example-airline.com/flights?from=SFO&to=ORD&date=2025-03-20",
},
]
monitor = FareMonitor()
results = monitor.check_fares(routes)
monitor.export_report()
Preisrückgänge erkennen und Läufe planen
Der eigentliche Wert einer Preisüberwachung liegt nicht im einzelnen Abruf, sondern im Verlauf. detect_price_drops vergleicht je Route den letzten mit dem vorletzten Minimalpreis und meldet Rückgänge oberhalb einer Schwelle – standardmäßig 5 %. So lösen Sie eine Benachrichtigung genau dann aus, wenn sich eine Route lohnt, statt Rohdaten zu durchforsten.
Für den Dauerbetrieb genügt ein Cron-Job. Ein Intervall von vier Stunden ist ein guter Startwert: Fluggesellschaften aktualisieren Preise in Wellen, häufigere Abfragen bringen kaum zusätzlichen Erkenntnisgewinn und erhöhen nur die Last.
# Check fares every 4 hours
0 */4 * * * cd /opt/fare-monitor && python fare_monitor.py
Was die Überwachung kostet
Weil CaptchaAI pro Thread und nicht pro Lösung abrechnet, skaliert die Preisüberwachung günstig: Solange die Abfragen über den Tag verteilt sind, reicht schon eine kleine Thread-Zahl. Die folgende Rechnung ordnet drei typische Ausbaustufen ein. Alle Beträge sind in US-Dollar angegeben.
| Umfang | Routen | Prüfungen/Tag | CAPTCHAs/Tag | Kosten (geschätzt) |
|---|---|---|---|---|
| Persönlich | 5 | 6/Route | ~30 | 0,50 $ |
| Kleine Agentur | 50 | 4/Route | ~200 | 2–5 $ |
| Unternehmen | 500 | 6/Route | ~3.000 | 20–40 $ |
Praxisbeispiel: DACH-Reise-Tech
Angenommen, ein kleines Reise-Tech-Team in Wien überwacht 80 Routen quer über mehrere OTAs, um seinen Kunden Preisalarme anzubieten. Die Worker laufen auf zwei Hetzner-VPS, ausgelöst über einen GitLab-CI-Scheduler – in deutschen Enterprises eine gängigere Kombination als reine GitHub-Actions-Setups. Der FareMonitor läuft alle zwei Stunden, detect_price_drops speist die Treffer in einen internen Alert-Kanal.
Wer im DACH-Raum Reisedaten sammelt, sollte die rechtliche Seite mitdenken: IP-Adressen können nach DSGVO personenbezogene Daten sein, und die AGB vieler Portale untersagen automatisierte Abfragen. Prüfen Sie robots.txt, Nutzungsbedingungen und die Rechtsgrundlage Ihrer Datenflüsse, bevor Sie produktiv gehen. Das ist Sorgfaltspflicht des Betreibers, keine Rechtsberatung – und in Deutschland baut ein konservativer, dokumentierter Umgang mit solchen Fragen mehr Vertrauen auf als jede Reichweite.
FAQ
Welche CAPTCHA-Typen tauchen auf Reiseportalen auf – und löst CaptchaAI sie?
reCAPTCHA v2 und v3, Cloudflare Turnstile und Cloudflare Challenge sowie klassische Bild-CAPTCHAs dominieren. Alle vier Familien löst CaptchaAI zuverlässig; hCaptcha wird nicht unterstützt und kommt auf Reiseseiten ohnehin selten vor.
Wie viele Threads brauche ich für die Preisüberwachung?
Für eine persönliche Watchlist mit wenigen Routen genügt BASIC (15 $/Monat, 5 Threads). Prüfen Sie Hunderte Routen häufig parallel, planen Sie mit ADVANCE (90 $/Monat, 50 Threads) oder höher – Threads bestimmen die gleichzeitige Kapazität, nicht die Zahl der Lösungen.
Ist das Scraping von Reisepreisen in Deutschland zulässig?
Das hängt vom Einzelfall ab. Prüfen Sie AGB und robots.txt des Portals und beachten Sie, dass IP-Adressen nach DSGVO personenbezogene Daten sein können. Klären Sie die Rechtsgrundlage, bevor Sie produktiv scrapen – dieser Leitfaden ersetzt keine Rechtsberatung.
Wie gehe ich mit JavaScript-lastigen Preisseiten um?
Rendern Sie die Seite mit Selenium oder Playwright und übergeben Sie nur die CAPTCHA-Abfrage an CaptchaAI. So erhalten Sie die dynamisch nachgeladenen Preise und lösen die Abfrage im selben Ablauf.