Trường Hợp Sử Dụng

So sánh giá giao đồ ăn bằng cách giải CAPTCHA

DoorDash tính phí giao hàng cao hơn Uber Eats bao nhiêu cho cùng một nhà hàng? Câu trả lời nằm sau một lớp CAPTCHA: reCAPTCHA v3, Cloudflare Turnstile và Cloudflare Challenge chặn phần lớn request tự động ngay tại trang tìm nhà hàng hoặc trang thực đơn. Bài này dành cho dev và đội price-intelligence cần vượt qua lớp bảo vệ đó để so sánh giá, phí giao hàng và khuyến mãi trên nhiều nền tảng cùng lúc.


CAPTCHA theo từng nền tảng giao đồ ăn

Nền tảng Loại CAPTCHA Yếu tố kích hoạt Dữ liệu được bảo vệ
DoorDash reCAPTCHA v3 + Cloudflare Phát hiện bot Thực đơn, giá cả, phí
Uber Eats Cloudflare Turnstile Truy cập tự động Danh sách nhà hàng, giá cả
Grubhub reCAPTCHA v2 Giới hạn tần suất request Thực đơn, khuyến mãi
Postmates Cloudflare Challenge Phát hiện scraping Phí giao hàng, ETA
Just Eat reCAPTCHA v2 Tìm kiếm lặp lại Dữ liệu nhà hàng
Instacart reCAPTCHA v3 Phát hiện bot Giá tạp hóa

CaptchaAI hỗ trợ đầy đủ các loại trong bảng trên.


Những chỉ số nên theo dõi khi so sánh giá

Sáu chỉ số hữu ích nhất cho một dashboard so sánh giá: giá từng món (chênh lệch, markup theo nền tảng), phí giao hàng, số lượng đặt hàng tối thiểu (rào cản tiếp cận), thời gian giao hàng ước tính, khuyến mãi/giảm giá và tình trạng nhà hàng có sẵn theo khu vực. Ghép cả sáu vào cùng một bản ghi cho phép so sánh trực tiếp một nhà hàng trên nhiều nền tảng thay vì chỉ nhìn giá đơn lẻ.


Vì sao pipeline chạy từ Việt Nam cần một API giải CAPTCHA ổn định

Đây là dạng dự án quen thuộc với các công ty outsourcing tại TP.HCM và Hà Nội nhận từ khách hàng Mỹ: theo dõi giá giao đồ ăn theo thời gian thực cho báo cáo price-intelligence. Server chạy từ Việt Nam gọi tới các nền tảng mobile-first như DoorDash, Uber Eats thường gặp CAPTCHA nhiều hơn IP nội địa Mỹ — múi giờ lệch còn khiến job ban đêm (giờ VN) trùng giờ cao điểm bên Mỹ, khi các nền tảng siết kiểm tra bot nhất. Một API giải CAPTCHA ổn định trong pipeline quan trọng hơn nhiều so với chạy nội bộ tại Mỹ.


Script Python so sánh giá đa nền tảng

Đoạn code dưới đây tìm nhà hàng, lấy thực đơn và so sánh phí giao hàng trên nhiều nền tảng, tự động giải CAPTCHA mỗi khi gặp reCAPTCHA hoặc Turnstile giữa chừng.

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

Thiết bị và mạng khi thu thập dữ liệu đa nền tảng

  • Dùng User-Agent thiết bị di động thật (iPhone Safari hoặc Android Chrome) khi target DoorDash, Uber Eats — hai nền tảng phát hiện bot chặt nhất.
  • Giữ User-Agent và session ổn định trong một lần chạy; đổi liên tục giữa các request cùng tác vụ là dấu hiệu bất thường với Cloudflare.
  • Đa dạng nguồn yêu cầu hữu ích khi mô phỏng người dùng nhiều khu vực, nhưng chỉ là hạ tầng — không thay thế việc giải CAPTCHA khi thử thách xuất hiện.
  • Grubhub, Instacart ít nhạy thiết bị hơn nhưng vẫn áp giới hạn tần suất; giãn thời gian giữa các lần gọi giúp giảm tỷ lệ gặp CAPTCHA.

Xử lý lỗi thường gặp khi thu thập giá

  • Không có kết quả nhà hàng — sai mã bưu chính hoặc gặp trang CAPTCHA; kiểm tra lại mã bưu chính.
  • Giá thực đơn khác giữa web và app — chênh lệch giá thật; dùng User-Agent di động để lấy giá tương đương app.
  • Lặp vòng thử thách Cloudflare — fingerprint thiết bị không nhất quán; giữ User-Agent và session ổn định.
  • Nhà hàng có trên nền tảng này nhưng không có nền tảng khác — độ phủ dữ liệu khác nhau; đánh dấu "không có sẵn".
  • Phí giao hàng không chính xác — giá phụ thuộc vị trí; khớp vị trí request với đúng địa chỉ mục tiêu.

Câu hỏi thường gặp

CaptchaAI hỗ trợ loại CAPTCHA nào trên DoorDash, Uber Eats, Grubhub?

Có — reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile và Cloudflare Challenge, đủ cho toàn bộ bảng nền tảng ở trên.

Vì sao giá món ăn trên các nền tảng giao hàng lại chênh lệch nhau?

Nhà hàng đặt giá khác nhau trên từng nền tảng để bù tỷ lệ hoa hồng, thường 15-30%, cộng phí giao hàng riêng của mỗi nền tảng.

Chạy script từ server tại Việt Nam có làm tăng tỷ lệ gặp CAPTCHA không?

Thường là có, vì IP ngoài nước Mỹ dễ bị đánh giá rủi ro cao hơn. Một API giải CAPTCHA ổn định giúp giữ tỷ lệ thành công đều đặn thay vì phụ thuộc vào IP gốc.

Nên so sánh giá bao lâu một lần?

Hàng tuần cho phân tích thị trường chung; hàng ngày trong đợt khuyến mãi lớn hoặc nghiên cứu cạnh tranh gấp rút.

Giải CAPTCHA cho nhiều nền tảng cùng lúc cần bao nhiêu thread?

Gói BASIC ($15/tháng, 5 thread) đủ cho vài nhà hàng trên 2-3 nền tảng; crawl diện rộng nhiều thành phố nên cân nhắc STANDARD ($30/tháng, 15 thread) trở lên.


Hướng dẫn liên quan


So sánh giá giao đồ ăn ở quy mô lớn — lấy API key CaptchaAI và tự động hóa việc phân tích đa nền tảng.

Os comentários estão desativados para este artigo.