Use Cases

Comparaison des prix de livraison de nourriture avec la résolution de CAPTCHA

Un même plat peut afficher trois tarifs différents selon l'application qui le propose : commission prélevée sur le restaurant, frais de service, promotions du moment. Mesurer ces écarts à grande échelle suppose d'interroger DoorDash, Uber Eats, Grubhub et leurs concurrents en continu — or ces plateformes verrouillent leurs pages de tarification derrière des CAPTCHA et de la détection de trafic automatisé. Ce guide montre comment résoudre reCAPTCHA v2, reCAPTCHA v3 et Cloudflare Turnstile via l'API CaptchaAI pour récupérer menus, frais de livraison et délais estimés sans rester bloqué sur un défi.


Pourquoi les prix de livraison de nourriture varient d'une plateforme à l'autre

La divergence de prix n'est pas un bug : c'est le modèle économique du secteur. Un restaurant qui paie 30 % de commission à une plateforme et 15 % à une autre répercute souvent la différence sur ses menus. À cela s'ajoutent des frais de livraison, des frais de service et des seuils de commande minimum propres à chaque application. Pour un service de veille tarifaire ou une étude de marché restauration, c'est précisément cette matrice de prix qu'il faut reconstituer.

Les techniques décrites ici valent aussi pour les marchés francophones, où Uber Eats, Deliveroo et Just Eat se partagent la France, la Belgique et la Suisse. Un point de vigilance : dès que vous collectez et stockez des données, appliquez le principe de minimisation du RGPD — ne conservez que les champs tarifaires utiles (prix, frais, délais) et évitez de capter des données personnelles sur les restaurateurs ou les livreurs.


Les CAPTCHA rencontrés sur les plateformes de livraison

Chaque plateforme choisit son mécanisme de protection selon son architecture et son niveau de trafic automatisé attendu. Le tableau ci-dessous résume les types les plus courants et ce qu'ils protègent.

Plateforme Type de CAPTCHA Déclencheur Données protégées
DoorDash reCAPTCHA v3 + Cloudflare Détection de trafic automatisé Menus, prix, frais
Uber Eats Cloudflare Turnstile Accès automatisé Fiches restaurants, prix
Grubhub reCAPTCHA v2 Limitation de débit Articles de menu, promotions
Postmates Cloudflare Challenge Détection de scraping Frais de livraison, délais
Just Eat reCAPTCHA v2 Recherches répétées Données restaurants
Instacart reCAPTCHA v3 Détection de trafic automatisé Prix des produits d'épicerie

CaptchaAI prend en charge chacun de ces types : reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile et Cloudflare Challenge. En pratique, votre code détecte le type présent sur la page, envoie le sitekey à l'API et réinjecte le token obtenu dans la requête.


Un comparateur de prix multiplateforme en Python

Le script suivant encapsule toute la logique : session HTTP avec agent utilisateur mobile, détection du CAPTCHA, résolution via CaptchaAI, puis extraction des restaurants et des menus. La méthode compare_restaurant_across_platforms recherche le même restaurant sur plusieurs applications et aligne les tarifs pour comparaison.

import requests
import time
import re
from bs4 import BeautifulSoup
import json

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY, "method": method,
        "googlekey": sitekey, "pageurl": pageurl, "json": 1,
    }
    data.update(kwargs)
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]
    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]
    raise TimeoutError("Timeout")


class FoodDeliveryComparator:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) "
            "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 "
            "Mobile/15E148 Safari/604.1",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def search_restaurants(self, platform_url, location, cuisine=None):
        """Search restaurants on a delivery platform."""
        params = {"address": location}
        if cuisine:
            params["cuisine"] = cuisine

        url = f"{platform_url}/search"
        resp = self.session.get(url, params=params, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, url)

        return self._parse_restaurants(resp.text)

    def get_menu(self, restaurant_url):
        """Get menu with prices from a specific restaurant."""
        resp = self.session.get(restaurant_url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, restaurant_url)

        return self._parse_menu(resp.text)

    def compare_restaurant_across_platforms(self, restaurant_name, platforms, location):
        """Compare same restaurant's pricing across delivery platforms."""
        results = []

        for platform in platforms:
            try:
                restaurants = self.search_restaurants(
                    platform["url"], location,
                )

                # Find matching restaurant
                match = None
                for r in restaurants:
                    if restaurant_name.lower() in r["name"].lower():
                        match = r
                        break

                if match and match.get("url"):
                    menu = self.get_menu(match["url"])
                    results.append({
                        "platform": platform["name"],
                        "restaurant": match["name"],
                        "delivery_fee": match.get("delivery_fee", ""),
                        "delivery_time": match.get("delivery_time", ""),
                        "menu_items": len(menu),
                        "sample_prices": menu[:5],
                    })
                else:
                    results.append({
                        "platform": platform["name"],
                        "restaurant": restaurant_name,
                        "status": "not found",
                    })

            except Exception as e:
                results.append({
                    "platform": platform["name"],
                    "error": str(e),
                })
            time.sleep(5)

        return results

    def track_delivery_fees(self, platforms, location, output_file):
        """Track delivery fees across platforms for analysis."""
        all_data = []

        for platform in platforms:
            try:
                restaurants = self.search_restaurants(
                    platform["url"], location,
                )
                for r in restaurants[:20]:  # Top 20 per platform
                    all_data.append({
                        "platform": platform["name"],
                        "restaurant": r["name"],
                        "delivery_fee": r.get("delivery_fee", ""),
                        "delivery_time": r.get("delivery_time", ""),
                        "rating": r.get("rating", ""),
                    })
                time.sleep(5)
            except Exception as e:
                print(f"Error on {platform['name']}: {e}")

        with open(output_file, "w") as f:
            json.dump(all_data, f, indent=2)

        return all_data

    def _has_captcha(self, html):
        return any(tag in html.lower() for tag in [
            'data-sitekey', 'g-recaptcha', 'cf-turnstile',
            'challenge-platform',
        ])

    def _solve_and_retry(self, html, url):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if not match:
            return self.session.get(url)
        sitekey = match.group(1)
        if 'cf-turnstile' in html:
            token = solve_captcha("turnstile", sitekey, url)
            return self.session.post(url, data={"cf-turnstile-response": token})
        token = solve_captcha("userrecaptcha", sitekey, url)
        return self.session.post(url, data={"g-recaptcha-response": token})

    def _parse_restaurants(self, html):
        soup = BeautifulSoup(html, "html.parser")
        restaurants = []
        for card in soup.select(".restaurant-card, .store-card, .merchant"):
            name_el = card.select_one(".name, .store-name, h3")
            if name_el:
                restaurants.append({
                    "name": name_el.get_text(strip=True),
                    "url": self._link(card),
                    "delivery_fee": self._text(card, ".delivery-fee, .fee"),
                    "delivery_time": self._text(card, ".delivery-time, .eta"),
                    "rating": self._text(card, ".rating, .stars"),
                })
        return restaurants

    def _parse_menu(self, html):
        soup = BeautifulSoup(html, "html.parser")
        items = []
        for item in soup.select(".menu-item, .item-card"):
            items.append({
                "name": self._text(item, ".item-name, .name"),
                "price": self._text(item, ".price, .item-price"),
                "description": self._text(item, ".description, .item-desc"),
            })
        return items

    def _text(self, el, selector):
        found = el.select_one(selector)
        return found.get_text(strip=True) if found else ""

    def _link(self, card):
        a = card.select_one("a")
        return a.get("href", "") if a else ""


# Usage
comparator = FoodDeliveryComparator(
    proxy="http://user:pass@mobile.proxy.com:5000"
)

# Compare platforms
platforms = [
    {"name": "Platform A", "url": "https://delivery-a.example.com"},
    {"name": "Platform B", "url": "https://delivery-b.example.com"},
    {"name": "Platform C", "url": "https://delivery-c.example.com"},
]

comparison = comparator.compare_restaurant_across_platforms(
    restaurant_name="Pizza Palace",
    platforms=platforms,
    location="10001",
)

for result in comparison:
    print(f"{result.get('platform')}: Fee={result.get('delivery_fee')} "
          f"ETA={result.get('delivery_time')}")

Le paramètre location attend un code postal du marché ciblé — les tarifs et la disponibilité dépendent de la zone de livraison. Adaptez-le à chaque ville testée.


Quel proxy retenir pour chaque plateforme

Les applications de livraison sont pensées d'abord pour le mobile. Un proxy mobile (4G) associé à un agent utilisateur iPhone ou Android produit le trafic le plus crédible et réduit fortement les défis répétés. Les plateformes à protection plus légère se contentent d'un proxy résidentiel.

Plateforme Proxy conseillé Raison
DoorDash Mobile (4G) Détection intensive, trafic mobile attendu
Uber Eats Mobile (4G) Plateforme pensée pour le mobile
Grubhub Résidentiel Protection standard
Instacart Résidentiel Détection modérée
Just Eat Résidentiel tournant Cloudflare standard

Les points de données à suivre

Une fois la collecte fiabilisée, ce sont ces champs qui portent la valeur analytique. Extrayez-les de façon structurée pour alimenter vos comparaisons.

Métrique Valeur métier
Prix des articles de menu Analyse de parité tarifaire et des majorations
Frais de livraison Comparaison des frais par plateforme
Montant minimum de commande Analyse des barrières d'accès
Délai de livraison estimé Comparaison des niveaux de service
Promotions et remises Veille marketing
Disponibilité des restaurants Analyse de couverture

Dépannage

Problème Cause Correctif
Aucun restaurant retourné Zone non desservie ou page CAPTCHA Renseignez le code postal de livraison exact
Prix du menu différents de l'application Écart de tarification entre le web et l'appli Utilisez un agent utilisateur mobile pour obtenir les tarifs applicatifs
Boucle de défi Cloudflare Empreinte incohérente Combinez proxy mobile et agent utilisateur mobile
Restaurant présent sur une plateforme, absent d'une autre Couverture différente Marquez « non disponible » dans la comparaison
Frais de livraison erronés Tarification liée à la zone Alignez la géolocalisation du proxy sur la zone cible

FAQ

Quels CAPTCHA CaptchaAI résout-il sur les plateformes de livraison ?

Les types les plus courants dans ce secteur : reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile et Cloudflare Challenge. CaptchaAI ne prend pas en charge hCaptcha ni FunCaptcha (Arkose Labs) ; si une plateforme y bascule, adaptez votre approche en conséquence.

Quel plan CaptchaAI choisir pour un comparateur multiplateforme ?

Tout dépend de votre parallélisme. Le plan BASIC ($15/mois, 5 threads) suffit pour une veille sur quelques plateformes ; le plan STANDARD ($30/mois, 15 threads) absorbe des collectes plus larges. La facturation est par thread simultané, avec des résolutions illimitées par thread.

Faut-il un proxy mobile ou résidentiel ?

Mobile pour DoorDash et Uber Eats, qui attendent un trafic issu d'applications mobiles ; résidentiel pour les plateformes à protection plus légère comme Grubhub. Faites toujours correspondre l'agent utilisateur au type de proxy.

La collecte des prix de livraison est-elle conforme au RGPD ?

La donnée tarifaire publique n'est pas une donnée personnelle, mais dès que vous stockez et croisez des informations, appliquez la minimisation : ne conservez que les champs utiles et vérifiez vos obligations RGPD avant toute exploitation.


Guides connexes

  • la surveillance des stocks en distribution
  • les proxys mobiles pour la résolution de CAPTCHA
  • la rotation des proxys résidentiels

Comparez les prix de livraison de nourriture à grande échelle : récupérez votre clé CaptchaAI et automatisez l'analyse multiplateforme.

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