Anwendungsfälle

Preisvergleich für Lebensmittellieferungen mit CAPTCHA-Lösung

Wer Menüpreise und Liefergebühren über mehrere Plattformen hinweg vergleicht, kommt an reCAPTCHA und Cloudflare Turnstile nicht vorbei. Der Ablauf, der trotzdem verlässlich Daten liefert, ist immer derselbe: Jede blockierte Anfrage geht an einen CAPTCHA-Lösungsdienst, das zurückgelieferte Token landet wieder im Request, und der Parser liest anschließend Preise, Gebühren und Lieferzeiten aus. Genau dafür ist dieser Leitfaden gedacht – für Preisvergleichsdienste, Marktforschung und Restaurant-Analytics, die DoorDash, Uber Eats, Grubhub und im DACH-Raum auch Lieferando oder Wolt strukturiert auswerten wollen.


Welche CAPTCHAs die Lieferdienste schützen

Lieferplattformen schirmen ihre Preisdaten mit unterschiedlichen Abfragetypen ab. Wer weiß, was wo auftaucht, wählt von vornherein den richtigen Lösungsweg.

Plattform CAPTCHA-Typ Auslöser Geschützte Daten
DoorDash reCAPTCHA v3 + Cloudflare Bot-Erkennung Menüs, Preise, Gebühren
Uber Eats Cloudflare Turnstile Automatisierter Zugriff Restauranteinträge, Preise
Grubhub reCAPTCHA v2 Rate-Limiting Menüpunkte, Aktionen
Postmates Cloudflare Challenge Scraping-Erkennung Liefergebühren, Lieferzeiten
Just Eat reCAPTCHA v2 Wiederholte Suchen Restaurantdaten
Instacart reCAPTCHA v3 Bot-Erkennung Lebensmittelpreise

CaptchaAI deckt genau diese Familien ab: reCAPTCHA v2 und v3, Cloudflare Turnstile und Cloudflare Challenge. hCaptcha und FunCaptcha gehören nicht dazu – sollte eine Plattform darauf umstellen, greift dieser Workflow nicht.


Welche Preisdaten sich zu erfassen lohnen

Bevor Sie das erste Skript starten, lohnt sich die Frage, welche Felder überhaupt einen Geschäftswert haben. Das hält den Datensatz schlank und den Traffic niedrig.

Kennzahl Geschäftswert
Preise einzelner Menüpunkte Preisparität und Aufschlagsanalyse
Liefergebühren Vergleich der Plattformgebühren
Mindestbestellwerte Analyse der Zugangsbarrieren
Geschätzte Lieferzeiten Service-Level-Vergleich
Aktionen und Rabatte Marketing-Intelligence
Restaurant-Verfügbarkeit Abdeckungsanalyse

Vergleichsskript für mehrere Plattformen

Das folgende Beispiel bündelt Suche, Menüabruf und CAPTCHA-Behandlung in einer Klasse. Erkennt der Code eine Abfrage, übermittelt er Sitekey und Page-URL an CaptchaAI, fügt das Token in den Folge-Request ein und liest die Seite danach normal aus.

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')}")

Ein Hinweis zur Rechtslage: Öffentliche Menüpreise sind das eine, personenbezogene Daten das andere. IP-Adressen und Standortangaben gelten nach DSGVO als personenbezogen. Prüfen Sie vor produktivem Einsatz Ihre Rechtsgrundlage und beschränken Sie die Erfassung auf öffentlich zugängliche Preis- und Gebührendaten.


Proxy-Wahl je Plattform

Lieferdienste sind konsequent „Mobile First" aufgebaut. Ein mobiler Proxy in Kombination mit einem mobilen User-Agent erzeugt daher den realistischsten Datenverkehr und reduziert die Zahl der ausgelösten Abfragen spürbar.

Plattform Empfohlener Proxy Grund
DoorDash Mobil (4G) Starke Bot-Erkennung, erwartet Mobilgeräte
Uber Eats Mobil (4G) Mobile-First-Plattform
Grubhub Residential Standardschutz
Instacart Residential Mäßige Bot-Erkennung
Just Eat Rotierendes Residential Standard-Cloudflare

Threads und Kosten für regelmäßige Vergleiche

CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung – innerhalb eines Tarifs lösen Sie also unbegrenzt viele CAPTCHAs. Für Preisvergleiche entscheidet die gewünschte Parallelität über den Tarif: Wie viele Plattformen und Standorte fragen Sie gleichzeitig ab?

  • BASIC (15 $/Monat, 5 Threads) – für gelegentliche Vergleiche einzelner Restaurants über wenige Plattformen.
  • STANDARD (30 $/Monat, 15 Threads) – für regelmäßige, parallele Läufe über mehrere Städte.
  • ADVANCE (90 $/Monat, 50 Threads) – für tägliche Marktabdeckung mit vielen Plattformen und Standorten.

Alle Preise in US-Dollar. Der Durchsatz hängt zusätzlich von der plattformspezifischen Lösungszeit ab; die aktuellen Tarife finden Sie auf der CaptchaAI-Preisseite.


Typische Fehler beim Scraping von Lieferpreisen

Problem Ursache Lösung
Leere Restaurantliste Standort nicht bedient oder CAPTCHA-Seite Korrekte Postleitzahl der Lieferadresse setzen
Menüpreise weichen von der App ab Unterschied zwischen Web- und App-Preisen Mobilen User-Agent nutzen, um App-äquivalente Preise zu erhalten
Wiederholte Cloudflare-Challenge Browser-Signale passen nicht zum Proxy Mobilen Proxy mit mobilem User-Agent kombinieren
Restaurant auf einer Plattform vorhanden, auf einer anderen nicht Unterschiedliche Abdeckung Im Vergleich als „nicht verfügbar" markieren
Falsche Liefergebühren Standortabhängige Preisgestaltung Proxy-Region an den Zielstandort anpassen

Häufige Fragen

Welche CAPTCHA-Typen tauchen bei Lieferplattformen auf?

Überwiegend reCAPTCHA v2 und v3 sowie Cloudflare Turnstile und Cloudflare Challenge. CaptchaAI löst genau diese Typen; hCaptcha und FunCaptcha werden nicht unterstützt.

Ist das Scraping von Lieferpreisen in Deutschland zulässig?

Öffentliche Preisdaten sind grundsätzlich weniger heikel als personenbezogene Daten. Da IP-Adressen und Standortangaben unter die DSGVO fallen, sollten Sie Ihre Rechtsgrundlage vorab prüfen und die Erfassung auf öffentlich einsehbare Menü- und Gebührendaten begrenzen.

Welcher CaptchaAI-Tarif passt zu einem Preisvergleich?

Das hängt an der Parallelität. Für vereinzelte Läufe reicht BASIC (15 $/Monat, 5 Threads), für tägliche Marktabdeckung über viele Plattformen eignet sich ADVANCE (90 $/Monat, 50 Threads). Die Zahl paralleler Anfragen bestimmt den Tarif, nicht die Menge der Lösungen.

Warum unterscheiden sich die Preise zwischen den Plattformen?

Restaurants kalkulieren je Plattform anders, um Provisionssätze von 15–30 % auszugleichen. Auch Liefer- und Servicegebühren variieren – genau das macht den plattformübergreifenden Vergleich überhaupt erst interessant.

Wie verhindere ich, dass mein Vergleichslauf blockiert wird?

Halten Sie das Anfragetempo niedrig und bauen Sie Pausen zwischen den Requests ein – das Skript wartet bewusst fünf Sekunden pro Plattform. Ein mobiler Proxy pro Region, ein passender User-Agent und die Erfassung nur der wirklich benötigten Felder senken die Zahl ausgelöster CAPTCHAs zusätzlich.


Verwandte Leitfäden


Lieferpreise im großen Maßstab vergleichen – holen Sie sich Ihren CaptchaAI-Schlüssel und automatisieren Sie die plattformübergreifende Analyse.

Kommentare sind für diesen Artikel deaktiviert.