Сценарии использования

Сравнение цен на доставку еды с решением CAPTCHA

Платформы доставки еды защищают свои данные о ценах с помощью CAPTCHA и обнаружения ботов. Службам сравнения цен, исследователям рынка и инструментам ресторанной аналитики необходим автоматический доступ для сравнения цен меню, стоимости доставки и рекламных акций на DoorDash, Uber Eats, Grubhub и других платформах.


CAPTCHA на платформах доставки

Платформа Тип капчи Курок Защищенные данные
ДверьДэш reCAPTCHA v3 + Cloudflare Обнаружение ботов Меню, цены, сборы
Убер Ест Cloudflare Turnstile Автоматизированный доступ Список ресторанов, цены
Грубхаб reCAPTCHA v2 Ограничение скорости Пункты меню, акции
Почтальоны страница Cloudflare-защиты в staging Обнаружение парсинга Стоимость доставки, расчетное время прибытия
Просто ешь reCAPTCHA v2 Повторные поиски Данные ресторана
Инстакарт reCAPTCHA v3 Обнаружение ботов Цены на продукты

Многоплатформенный компаратор цен

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

Рекомендации по прокси

Платформа подходящий прокси Почему
ДверьДэш Мобильная связь (4G) Обнаружение тяжелых ботов, ожидается мобильная версия
Убер Ест Мобильная связь (4G) Мобильная платформа
Грубхаб Жилой Стандартная защита
Инстакарт Жилой Умеренное обнаружение ботов
Просто ешь Вращающийся жилой дом Стандартный Cloudflare

Приложения доставки ориентированы на мобильные устройства — авторизованный сетевой выход с мобильными пользовательскими агентами дают наилучшие результаты.


Точки данных для отслеживания

Метрика Ценность бизнеса
Цены на позиции меню Анализ паритета цен и наценки
Стоимость доставки Сравнение комиссий платформы
Минимальная сумма заказа Анализ барьеров доступа
Ориентировочное время доставки Сравнение уровня обслуживания
Акции/discounts Маркетинговая разведка
Наличие ресторана Анализ покрытия

Поиск неисправностей

Проблема Причина Исправить
Пустые результаты ресторана Местоположение не обслуживается или страница CAPTCHA Установите правильный почтовый адрес доставки
Цены в меню отличаются от приложения Разница в ценах на веб-сайт и на приложение Используйте мобильный UA, чтобы получить цены, эквивалентные приложениям
Цикл испытаний Cloudflare Несовпадение сигналы браузера Используйте авторизованный сетевой выход + мобильный UA
Ресторан найден на одной платформе, но не найден на другой Различное покрытие Отметить как «недоступно» для сравнения
Неправильная стоимость доставки Цены в зависимости от местоположения Сопоставьте геолокацию прокси с целевым местоположением

Часто задаваемые вопросы

Почему цены на разных платформах доставки различаются?

Рестораны устанавливают разные цены на каждую платформу, чтобы учесть разную комиссию (15–30%). Стоимость доставки и плата за обслуживание также различаются в зависимости от платформы.

Должен ли я использовать мобильный или настольный компьютер для очистки приложений доставки?

Мобильные устройства — это платформы, ориентированные прежде всего на мобильные устройства. авторизованный сетевой выход-сервер с пользовательским агентом iPhone/Android производит наиболее аутентичный трафик.

Как часто мне следует сравнивать цены?

Еженедельно для общего анализа рынка. Ежедневно во время рекламных периодов или соревновательных исследовательских спринтов.


Связанные руководства

  • Мониторинг розничных товарных запасов
  • авторизованный сетевой выход для CAPTCHA
  • Ротация авторизованный сетевой выход

  • Сравните цены на доставку еды в масштабе —получите ключ CaptchaAIи автоматизируйте кросс-платформенный анализ.*
Комментарии для этой статьи отключены.