Cas d'Usage

Surveillance des tarifs aériens sur des sites protégés par CAPTCHA

Les sites de voyage et de compagnies aériennes utilisent souvent des CAPTCHA pour limiter les vérifications automatiques de prix. Dès qu'un système surveille plusieurs routes, il finit par rencontrer des reCAPTCHA, des pages interstitielles Cloudflare et des mécanismes de rate limiting. CaptchaAI prend en charge l'étape de résolution pour que le pipeline de monitoring continue à collecter des tarifs au lieu de s'arrêter à chaque challenge.

Ce guide montre comment construire un workflow qui détecte les CAPTCHA pendant les contrôles de prix, les traite proprement puis reprend l'extraction sans casser toute la chaîne.


Workflow de surveillance

Schedule check -> Request fare page -> CAPTCHA detected?
                                         -> Yes
                                    Solve via CaptchaAI -> Inject token -> Retry request
                                         -> No
                                    Parse fare data -> Store -> Alert on price change

Ce qu'il vous faut

Exigence Details
Clé API CaptchaAI captchaai.com
Python 3.8+ Avec requests
Proxy Idéalement résidentiel pour les sites de voyage
pip install requests

Helper de résolution CaptchaAI

import requests
import time

API_KEY = "YOUR_API_KEY"


def solve_recaptcha_v2(sitekey, pageurl):
    """Solve reCAPTCHA v2 and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "userrecaptcha",
        "googlekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(20)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")


def solve_turnstile(sitekey, pageurl):
    """Solve Cloudflare Turnstile and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "turnstile",
        "sitekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(10)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")

Monitorer les tarifs avec gestion des CAPTCHA

import json
from datetime import datetime


class FareMonitor:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {
                "http": f"http://{proxy}",
                "https": f"http://{proxy}"
            }
        self.fare_history = {}

    def check_fare(self, route):
        """Check fare for a route, solving CAPTCHAs if needed."""
        url = route["url"]

        response = self.session.get(url)

        # Detect CAPTCHA in response
        if self._has_recaptcha(response.text):
            sitekey = self._extract_sitekey(response.text)
            token = solve_recaptcha_v2(sitekey, url)
            response = self.session.post(url, data={
                "g-recaptcha-response": token,
                **route.get("params", {})
            })

        elif self._has_turnstile(response.text):
            sitekey = self._extract_turnstile_key(response.text)
            token = solve_turnstile(sitekey, url)
            response = self.session.post(url, data={
                "cf-turnstile-response": token,
                **route.get("params", {})
            })

        return self._parse_fare(response.text, route)

    def _has_recaptcha(self, html):
        return "g-recaptcha" in html or "recaptcha/api" in html

    def _has_turnstile(self, html):
        return "cf-turnstile" in html or "turnstile" in html

    def _extract_sitekey(self, html):
        # Extract data-sitekey from reCAPTCHA div
        if 'data-sitekey="' in html:
            start = html.index('data-sitekey="') + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _extract_turnstile_key(self, html):
        if 'data-sitekey="' in html:
            idx = html.index("cf-turnstile")
            start = html.index('data-sitekey="', idx) + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _parse_fare(self, html, route):
        """Parse fare data from the response. Customize per target site."""
        # Placeholder - implement per site
        return {
            "route": route["name"],
            "timestamp": datetime.now().isoformat(),
            "raw_length": len(html)
        }

    def monitor_routes(self, routes):
        """Check all routes and report price changes."""
        results = []
        for route in routes:
            try:
                fare = self.check_fare(route)
                results.append(fare)
                print(f"[OK] {route['name']}: checked")
            except Exception as e:
                print(f"[ERROR] {route['name']}: {e}")
        return results


# Usage
routes = [
    {
        "name": "NYC-LAX",
        "url": "https://example-airline.com/search?from=JFK&to=LAX&date=2025-08-15",
        "params": {"adults": 1}
    },
    {
        "name": "SFO-ORD",
        "url": "https://example-airline.com/search?from=SFO&to=ORD&date=2025-08-20",
        "params": {"adults": 1}
    }
]

monitor = FareMonitor(proxy="user:pass@proxy.example.com:8080")
results = monitor.monitor_routes(routes)

for r in results:
    print(json.dumps(r, indent=2))

L'idée pratique ici est de ne pas réduire chaque vérification à un simple succès ou échec. Il est utile de journaliser si un CAPTCHA a été résolu, quel type de challenge a été rencontré et si le parseur a vraiment extrait un tarif exploitable. C'est ce qui rend la surveillance actionnable pour des alertes de prix ou des comparaisons multi-routes.


À quelle cadence vérifier les prix ?

Type de route Cadence raisonnable Pourquoi
Routes très volatiles ou à forte valeur Toutes les quelques heures Les variations de prix méritent une veille plus serrée
Routes loisirs plus stables 1 à 2 fois par jour La pression sur le site et les proxys reste modérée
Gros portefeuille de routes Batchs étalés La charge est plus régulière et déclenche moins de CAPTCHA
Alertes ultra agressives À utiliser avec prudence Le coût et le risque de blocage montent très vite

Planifier les contrôles

Lancez le moniteur avec cron ou un planificateur équivalent :

# Check fares every 6 hours
0 */6 * * * cd /path/to/project && python fare_monitor.py >> /var/log/fares.log 2>&1

Dépannage

Problème Cause probable Action recommandée
CAPTCHA très fréquents Trop de requêtes depuis la même IP Utilisez des proxys résidentiels rotatifs
Prix obsolètes Réponses mises en cache Ajoutez des headers anti-cache ou faites varier certains paramètres
IP bloquée Rate limiting du site Augmentez le délai entre les contrôles et faites tourner les proxys
Échec de résolution CAPTCHA Mauvaise extraction du sitekey Vérifiez que le sitekey correspond bien au challenge présent sur la page

FAQ

À quelle fréquence faut-il vérifier les tarifs ?

Un contrôle toutes les 4 à 8 heures est un bon point de départ pour beaucoup de cas. Vérifier plus souvent augmente généralement la fréquence des CAPTCHA et le coût des proxys.

Quels types de CAPTCHA sont fréquents sur les sites de compagnies aériennes ?

Les plus courants sont reCAPTCHA v2, Cloudflare Turnstile et certaines pages de challenge Cloudflare. Quelques sites utilisent aussi des CAPTCHA image de manière plus ponctuelle.

Faut-il des proxys résidentiels ?

Oui, dans la plupart des cas. Les sites de voyage bloquent souvent les IP de datacenter, alors que les proxys résidentiels ou mobiles donnent de meilleurs taux de réussite.

Peut-on surveiller plusieurs compagnies aériennes ?

Oui. Il faut adapter la méthode _parse_fare au HTML ou au format de réponse de chaque site puis ajouter les routes correspondantes.

Comment gérer les pages Cloudflare Challenge ?

Utilisez method=cloudflare_challenge avec un proxy cohérent. Le cookie cf_clearance renvoyé ouvre ensuite l'accès au site. Consultez aussi le guide dédié à Cloudflare Challenge.


Guides associes


Si vous surveillez un grand volume de routes, obtenez votre clé CaptchaAI et intégrez la résolution CAPTCHA comme une étape normale du pipeline plutôt que comme un cas d'exception.

Les commentaires sont désactivés pour cet article.