Flugstatus-Portale beantworten wiederholte automatisierte Abfragen mit einer CAPTCHA-Abfrage: Cloudflare Turnstile, Cloudflare Challenge oder reCAPTCHA schieben sich zwischen Ihren Scraper und die Echtzeitdaten zu Verspätungen, Gate-Wechseln und Annullierungen. Die Antwort darauf ist kein aggressiveres Polling, sondern ein Monitoring-Client, der die Abfrage erkennt, das Token über CaptchaAI löst und den Abruf in derselben Sitzung fortsetzt. Dieser Leitfaden zeigt genau diese Architektur – von der Erkennung über das Lösen bis zur Intervallsteuerung, die gar nicht erst unnötig viele CAPTCHAs auslöst.
Wo CAPTCHAs auf Flug- und Flughafenportalen auftauchen
Bevor Sie Code schreiben, lohnt sich der Überblick, an welcher Stelle im Abruf die Abfrage überhaupt erscheint. Das bestimmt, welchen CAPTCHA-Typ Sie lösen müssen und mit welchem Feldnamen Sie das Token zurückschicken.
| Portaltyp | CAPTCHA | Auslöser |
|---|---|---|
| Flugstatusseite der Fluggesellschaft | Cloudflare Turnstile | Häufige Anfragen von derselben IP |
| Ankunfts-/Abflugtafeln der Flughäfen | Cloudflare Challenge | Bot-Erkennung |
| Flugsuchmaschinen | reCAPTCHA v2/v3 | Übermittlung des Suchformulars |
| Buchungsstatus-Prüfung | reCAPTCHA v2 | Vor Anzeige der Reiseroute |
| API-Rate-Limit-Seiten | Benutzerdefiniertes CAPTCHA | Nach Überschreiten der Anfragelimits |
Ein typisches DACH-Szenario: Sie verfolgen einen Eurowings-Flug ab Düsseldorf oder lesen die Abflugtafel am Flughafen München (MUC) aus. Nach einigen schnellen Aufrufen von derselben IP liefert das Portal statt der Statusseite eine Turnstile-Abfrage aus. Ein Monitoring-Client muss diesen Fall als Normalzustand behandeln, nicht als Fehler. Beachten Sie dabei die Nutzungsbedingungen der jeweiligen Quelle und ein angemessenes Intervall – öffentlich einsehbare Flugdaten abzufragen ist etwas anderes, als ein Portal mit Anfragen zu überlasten.
Flugstatus-Monitoring: die Architektur
Der Kern ist eine Klasse, die eine requests.Session() wiederverwendet, jede Antwort auf eine CAPTCHA-Seite prüft und bei Bedarf löst. Entscheidend ist die Erkennung: Ein Status 403 oder die Marker cf-turnstile bzw. g-recaptcha im HTML signalisieren, dass zuerst ein Token benötigt wird. Erst danach parst der Client die Flugdaten.
import requests
import time
from datetime import datetime
class FlightMonitor:
def __init__(self, api_key):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def check_flight(self, airline_url, flight_number):
"""Check flight status, handling CAPTCHAs if encountered."""
response = self.session.get(
f"{airline_url}/flight-status/{flight_number}"
)
if self._is_captcha_page(response):
response = self._solve_and_retry(response, airline_url)
return self._parse_flight_data(response.text)
def _is_captcha_page(self, response):
return (
response.status_code == 403 or
"cf-turnstile" in response.text or
"g-recaptcha" in response.text
)
def _solve_and_retry(self, response, url):
import re
# Detect CAPTCHA type
if "cf-turnstile" in response.text:
match = re.search(r'data-sitekey="(0x[^"]+)"', response.text)
token = self._solve_turnstile(match.group(1), url)
field = "cf-turnstile-response"
else:
match = re.search(r'data-sitekey="([^"]+)"', response.text)
token = self._solve_recaptcha(match.group(1), url)
field = "g-recaptcha-response"
return self.session.post(url, data={field: token})
def _solve_turnstile(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "turnstile",
"sitekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
return self._poll_result(task_id)
def _solve_recaptcha(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
return self._poll_result(task_id)
def _poll_result(self, task_id):
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("CAPTCHA solve timed out")
def _parse_flight_data(self, html):
# Parse flight status from HTML
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_none(node):
return node.text.strip() if node and node.text else None
return {
"status": text_or_none(soup.select_one(".flight-status")),
"departure": text_or_none(soup.select_one(".departure-time")),
"arrival": text_or_none(soup.select_one(".arrival-time")),
"gate": text_or_none(soup.select_one(".gate-info")),
"checked_at": datetime.now().isoformat()
}
Turnstile nutzt den Feldnamen cf-turnstile-response, reCAPTCHA g-recaptcha-response – die Zuordnung im Code entscheidet, ob der erneute POST akzeptiert wird. Das Polling fragt das Ergebnis in Abständen ab und bricht nach einem Timeout kontrolliert ab, statt endlos zu blockieren.
Regelmäßige Flugüberwachung mit CAPTCHA-Handling
Für die eigentliche Überwachung läuft check_flight in einer Schleife mit festem Intervall. Der Client führt eine Historie, meldet Statuswechsel und fängt Fehler pro Durchlauf ab, damit ein einzelner Fehlschlag die gesamte Überwachung nicht abbricht.
def monitor_flight(monitor, airline_url, flight_number,
interval_seconds=300, max_checks=48):
"""Monitor a flight every N seconds, handling CAPTCHAs as needed."""
history = []
for check_num in range(max_checks):
try:
status = monitor.check_flight(airline_url, flight_number)
history.append(status)
# Alert on changes
if len(history) > 1 and status["status"] != history[-2]["status"]:
print(f"Status changed: {history[-2]['status']} → {status['status']}")
print(f"Check {check_num + 1}: {status['status']} "
f"(Gate: {status.get('gate', 'Coming soon')})")
except Exception as e:
print(f"Check {check_num + 1} failed: {e}")
time.sleep(interval_seconds)
return history
# Usage
monitor = FlightMonitor("YOUR_API_KEY")
monitor_flight(monitor, "https://airline.example.com", "AA1234")
Mehrere Fluggesellschaften parallel überwachen (JavaScript)
Wer mehrere Flüge über verschiedene Airlines hinweg verfolgt, hält am besten eine Map pro Flugnummer und löst die Turnstile-Abfrage für jede Quelle unabhängig. So bleibt eine blockierte Airline ohne Folgen für die übrigen.
class FlightTracker {
constructor(apiKey) {
this.apiKey = apiKey;
this.flights = new Map();
}
async addFlight(airline, flightNumber, checkUrl) {
this.flights.set(flightNumber, {
airline,
url: checkUrl,
history: [],
lastCheck: null
});
}
async checkAll() {
const results = [];
for (const [flightNum, flight] of this.flights) {
try {
const status = await this.checkFlight(flight.url, flightNum);
flight.history.push(status);
flight.lastCheck = new Date();
results.push({ flight: flightNum, ...status });
} catch (error) {
results.push({ flight: flightNum, error: error.message });
}
}
return results;
}
async checkFlight(url, flightNumber) {
const response = await fetch(`${url}/status/${flightNumber}`);
const html = await response.text();
// Check for CAPTCHA
if (html.includes('cf-turnstile') || response.status === 403) {
return this.solveAndRetry(url, flightNumber, html);
}
return this.parseStatus(html);
}
async solveAndRetry(url, flightNumber, html) {
const siteKeyMatch = html.match(/data-sitekey="(0x[^"]+)"/);
if (!siteKeyMatch) throw new Error('No sitekey found');
const token = await this.solveTurnstile(siteKeyMatch[1], url);
const response = await fetch(`${url}/status/${flightNumber}`, {
method: 'POST',
body: new URLSearchParams({ 'cf-turnstile-response': token })
});
return this.parseStatus(await response.text());
}
}
Prüfintervall und CAPTCHA-Rate im Verhältnis
Die wichtigste Stellschraube ist das Intervall: Je kürzer der Abstand, desto häufiger die Abfrage. Die folgenden Richtwerte helfen, eine Balance zwischen Aktualität und Aufwand zu finden.
| Prüfintervall | Typische CAPTCHA-Rate | Empfehlung |
|---|---|---|
| Jede Minute | Hoch (50–80 %) | Zu aggressiv – Intervall verlängern |
| Alle 5 Minuten | Mäßig (10–30 %) | Für kritische Flüge akzeptabel |
| Alle 15 Minuten | Niedrig (5–10 %) | Gute Balance für die Routineüberwachung |
| Alle 30 Minuten | Sehr niedrig (< 5 %) | Am besten für die Langzeitverfolgung |
| Stündlich | Minimal (< 1 %) | CAPTCHAs werden selten ausgelöst |
Für einen Flug am Reisetag sind 5 Minuten sinnvoll, für die Beobachtung über mehrere Tage reichen 30 Minuten. In der Praxis ist nicht das Lösen der Engpass, sondern das Rate-Limiting der Quelle.
CAPTCHA-Begegnungen über die Sitzung reduzieren
Sie senken die Zahl der Abfragen deutlich, indem Sie den Sitzungsstatus konsequent beibehalten. Cloudflare vergibt nach einer gelösten Abfrage ein cf_clearance-Cookie, das kurzzeitig weitere Abfragen erspart.
| Technik | Wirkung |
|---|---|
| Cookies zwischen den Prüfungen beibehalten | Cloudflare cf_clearance bleibt 15–30 Minuten gültig |
| Konsistenten User-Agent verwenden | Ein wechselnder User-Agent löst neue Abfragen aus |
| Proxy-Konsistenz wahren | Gleiche IP sorgt für stabile Cloudflare-Sitzungen |
| Anfragen gleichmäßig verteilen | Burst-Muster lösen Rate-Limiting aus |
Ein Hinweis zum Datenschutz: Sobald Sie mit Proxys arbeiten, verarbeiten Sie IP-Adressen – nach DSGVO personenbezogene Daten. Prüfen Sie Ihre Rechtsgrundlage und Datenflüsse, wenn Sie Flugdaten in größerem Umfang erheben.
Kosten: Thread-basierte Abrechnung statt Preis pro Lösung
CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro gelöster CAPTCHA – jeder Tarif enthält unbegrenzte Lösungen pro Thread. Für die Überwachung einzelner Flüge genügt der BASIC-Tarif (15 $/Monat, 5 Threads); wer viele Airlines parallel abfragt, wählt STANDARD (30 $/Monat, 15 Threads) oder ADVANCE (90 $/Monat, 50 Threads). Preise verstehen sich in US-Dollar. Ein einzelner Monitoring-Prozess braucht selten mehr als einen aktiven Thread gleichzeitig, weil zwischen den Abfragen längere Pausen liegen.
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Zu viele CAPTCHAs in kurzer Zeit | Abrufrate oder Parallelität ist für die Quelle zu aggressiv | Verlängern Sie das Intervall, halten Sie die Sitzung stabil und prüfen Sie die Qualität Ihrer Proxys |
| Daten fehlen trotz gelöster CAPTCHA | Der Parser liest eine alte oder unvollständige Ansicht aus | Extrahieren Sie Daten erst nach erfolgreicher Token-Anwendung in derselben Sitzung |
| Cloudflare-Sperre (Fehler 1020) | Zu viele Anfragen in zu kurzer Zeit | Erhöhen Sie das Prüfintervall und verteilen Sie die Aufrufe gleichmäßig |
| Kosten steigen stärker als erwartet | Zu viele Wiederholungen oder unnötige Seitenaufrufe lösen zusätzliche Abfragen aus | Lösen Sie nur kritische Schritte und protokollieren Sie Wiederholungen pro Quelle |
Häufige Fragen
Welche CAPTCHA-Typen tauchen bei Flugportalen am häufigsten auf?
Meist Cloudflare Turnstile auf Statusseiten und Cloudflare Challenge auf Flughafentafeln; Flugsuchmaschinen setzen zusätzlich reCAPTCHA v2/v3 ein. CaptchaAI löst alle diese Typen mit hoher Erfolgsquote – entscheidend ist, dass Ihr Code den richtigen Feldnamen zurückschickt.
Kostet jede gelöste CAPTCHA bei CaptchaAI extra?
Nein. Die Abrechnung erfolgt Thread-basiert, nicht pro Lösung – jeder Tarif enthält unbegrenzte Lösungen pro Thread. Ein Monitoring-Job mit einem aktiven Thread bleibt damit auch bei vielen Abfragen im Rahmen des BASIC-Tarifs (15 $/Monat).
Was passiert, wenn das Turnstile-Token vor dem Datenabruf abläuft?
Turnstile-Token sind nur kurz gültig. Lösen Sie die Abfrage deshalb direkt vor dem POST und verwenden Sie das Token sofort in derselben Sitzung. Läuft es dennoch ab, erkennt der Client die erneute CAPTCHA-Seite und löst automatisch nach.
Darf ich Flugdaten überhaupt automatisiert abfragen?
Öffentlich einsehbare Flugstatusdaten abzurufen ist grundsätzlich legitim, doch prüfen Sie die Nutzungsbedingungen der Quelle und halten Sie ein rücksichtsvolles Intervall ein. Sobald Proxys und damit IP-Adressen ins Spiel kommen, gilt zusätzlich die DSGVO – dokumentieren Sie Ihre Rechtsgrundlage.