Un moniteur de vol fiable interroge les portails des compagnies et des aéroports toutes les quelques minutes, et c'est ce rythme qui réveille les protections anti-automatisation. Cloudflare Turnstile, le Cloudflare Challenge et reCAPTCHA v2/v3 se déclenchent dès qu'un portail repère des requêtes répétées depuis la même adresse. Voici comment les résoudre à la volée et garder un suivi des retards, annulations et changements de porte qui tient dans la durée.
Où les CAPTCHA se déclenchent sur les portails de vol
| Type de portail | CAPTCHA | Déclencheur |
|---|---|---|
| Page d'état des vols d'une compagnie | Cloudflare Turnstile | requêtes fréquentes depuis la même IP |
| Tableaux d'arrivées/départs d'aéroport | Cloudflare Challenge | détection de trafic automatisé |
| Moteurs de recherche de vols | reCAPTCHA v2/v3 | envoi du formulaire de recherche |
| Vérification du statut d'une réservation | reCAPTCHA v2 | avant l'affichage de l'itinéraire |
| Pages de limite de débit d'API | CAPTCHA personnalisé | dépassement des quotas de requêtes |
Un cas concret : suivre un vol Paris–Montréal
Prenons un opérateur data qui surveille les long-courriers entre Paris-Charles-de-Gaulle et Montréal-Trudeau. Le portail de la compagnie place l'horaire derrière un Cloudflare Turnstile ; les tableaux d'Aéroports de Paris passent en Cloudflare Challenge après quelques requêtes. Côté RGPD, ce suivi ne collecte que des données opérationnelles (numéro de vol, horaire, porte), aucune donnée personnelle. CaptchaAI prend en charge ces trois familles ; hCaptcha et FunCaptcha ne sont pas pris en charge.
Réduire les rencontres CAPTCHA en soignant la session
Le meilleur moyen d'en croiser moins reste de préserver l'état de la session entre les vérifications :
- Cookies persistants : le
cf_clearancede Cloudflare reste valable 15–30 min. - User-Agent constant : en changer relance de nouveaux défis.
- Proxy stable : une IP constante réduit les soupçons.
- Requêtes espacées : les rafales déclenchent la limitation de débit.
Choisir la bonne fréquence de vérification
| Fréquence de vérification | Taux de CAPTCHA typique | Recommandation |
|---|---|---|
| Toutes les minutes | Élevé (50–80 %) | Trop agressif — augmentez l'intervalle |
| Toutes les 5 minutes | Modéré (10–30 %) | Acceptable pour les vols critiques |
| Toutes les 15 minutes | Faible (5–10 %) | Bon équilibre pour une surveillance de routine |
| Toutes les 30 minutes | Très faible (< 5 %) | Idéal pour le suivi longue durée |
| Toutes les heures | Minime (< 1 %) | Les CAPTCHA se déclenchent rarement |
Construire le moniteur de vol en Python
Face à un portail protégé, le moniteur suit toujours le même cycle :
- Détecter le défi (statut 403 ou marqueur
cf-turnstile). - Envoyer le sitekey avec la bonne
method, puis interrogerres.php. - Rejouer la requête avec le token dans
cf-turnstile-responseoug-recaptcha-response.
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_of(node):
return node.get_text(strip=True) if node else None
return {
"status": text_of(soup.select_one(".flight-status")),
"departure": text_of(soup.select_one(".departure-time")),
"arrival": text_of(soup.select_one(".arrival-time")),
"gate": text_of(soup.select_one(".gate-info")),
"checked_at": datetime.now().isoformat(),
}
Boucler la surveillance à intervalle régulier
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")
Suivre plusieurs compagnies en JavaScript
Chaque vérification simultanée occupe un thread. Le plan BASIC ($15/mois, 5 threads) suffit pour une poignée de vols ; montez en gamme pour des dizaines de portails en parallèle. La facturation au thread inclut des résolutions illimitées : la cadence ne coûte rien de plus.
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());
}
}
Dépannage
- Un CAPTCHA à chaque vérification — session non persistée : réutilisez
requests.Session(). - Blocage Cloudflare (erreur 1020) — trop de requêtes : augmentez l'intervalle.
- Données de vol périmées après le CAPTCHA — token expiré pendant la résolution : résolvez juste à temps.
- Données différentes de celles du navigateur — rendu JavaScript manquant : passez par l'automatisation du navigateur.
FAQ
Les questions les plus fréquentes sur le suivi de vol automatisé.
Quel CAPTCHA rencontre-t-on le plus sur les portails de vol ?
Le Cloudflare Turnstile domine sur les pages d'état des compagnies, devant le Cloudflare Challenge des tableaux d'aéroport et reCAPTCHA v2/v3 sur les moteurs de recherche. CaptchaAI prend en charge ces trois familles.
CaptchaAI gère-t-il le Cloudflare Challenge des tableaux d'affichage ?
Oui, au même titre que Turnstile et reCAPTCHA. En revanche, CaptchaAI ne prend pas en charge hCaptcha ni FunCaptcha : si un portail les utilise, prévoyez une autre méthode.
Comment éviter de faire bloquer mon adresse IP pendant la surveillance ?
Espacez les vérifications (5 à 15 minutes), réutilisez la même session avec un proxy stable et gardez un User-Agent cohérent. CaptchaAI résout Turnstile avec un taux de réussite élevé ; le vrai facteur limitant reste la limitation de débit du portail, pas la résolution.
Articles connexes
- Comparatif GeeTest v3 et Cloudflare Turnstile
- Résoudre une erreur 403 Turnstile après injection du token
- Les modes du widget Cloudflare Turnstile
Prochaines étapes
Passez d'un suivi fragile à un moniteur fiable : récupérez votre clé API CaptchaAI et laissez les CAPTCHA des portails aériens se résoudre automatiquement.