Use Cases

Perbandingan Harga Pengiriman Makanan dengan Solve CAPTCHA

Untuk membangun pembanding harga food delivery yang benar-benar berguna, Anda hanya perlu menaklukkan satu hambatan teknis yang muncul berulang di DoorDash, Uber Eats, hingga Grubhub: CAPTCHA. Begitu reCAPTCHA v2/v3 dan Cloudflare Turnstile bisa ditangani secara otomatis, sisanya tinggal mengambil dan membandingkan data menu. Panduan ini menunjukkan pola integrasi CaptchaAI untuk agregasi harga lintas platform — relevan bagi tim riset pasar, agensi price monitoring, maupun developer freelance yang merawat tracker harga untuk klien. Fokusnya bukan trik rumit, melainkan alur yang stabil dan mudah dirawat: deteksi CAPTCHA, kirim ke solver, ambil token, lalu lanjutkan request.


CAPTCHA yang muncul di tiap platform delivery

Setiap platform memilih mekanisme proteksi yang berbeda, dan tipe CAPTCHA-nya menentukan method mana yang Anda panggil di CaptchaAI. Berikut peta ringkasnya.

Platform Tipe CAPTCHA Pemicu Data yang dilindungi
DoorDash reCAPTCHA v3 + Cloudflare Deteksi bot Menu, harga, biaya
Uber Eats Cloudflare Turnstile Akses otomatis Daftar restoran, harga
Grubhub reCAPTCHA v2 Rate limiting Item menu, promosi
Postmates Cloudflare Challenge Deteksi scraping Biaya pengiriman, ETA
Just Eat reCAPTCHA v2 Pencarian berulang Data restoran
Instacart reCAPTCHA v3 Deteksi bot Harga bahan makanan

Keempat tipe di tabel ini — reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, dan Cloudflare Challenge — termasuk yang didukung penuh (GA) oleh CaptchaAI. Artinya satu API key sudah cukup untuk menangani seluruh lini platform di atas tanpa berpindah layanan.


Alur integrasi: satu pola untuk semua platform

Pola yang dipakai sama untuk semua tipe CAPTCHA — yang berbeda hanya parameter method:

  1. Deteksi apakah halaman memuat CAPTCHA (cari penanda data-sitekey, g-recaptcha, atau cf-turnstile).
  2. Kirim task ke endpoint in.php beserta sitekey dan URL halaman, lalu simpan task ID yang dikembalikan.
  3. Lakukan polling ke res.php sampai token siap. reCAPTCHA v3 biasanya selesai dalam hitungan detik, Cloudflare Turnstile umumnya di bawah 10 detik, sedangkan reCAPTCHA v2 bisa memakan waktu lebih lama.
  4. Sisipkan token yang didapat (g-recaptcha-response untuk reCAPTCHA, cf-turnstile-response untuk Turnstile) ke request berikutnya, lalu lanjutkan pengambilan menu.

Karena CaptchaAI menagih per thread konkuren — bukan per solve — Anda bisa menjalankan beberapa platform secara paralel tanpa khawatir biaya membengkak seiring naiknya volume.


Pembanding harga multi-platform dengan Python

Kelas FoodDeliveryComparator berikut membungkus seluruh alur di atas: ia menjalankan pencarian restoran, mengambil menu, dan otomatis memanggil solver begitu mendeteksi CAPTCHA. Method compare_restaurant_across_platforms mencari restoran yang sama di beberapa platform sekaligus, sementara track_delivery_fees mengumpulkan biaya antar untuk analisis lanjutan.

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

Perhatikan header User-Agent iPhone dan jeda time.sleep(5) antar platform. Keduanya membuat pola request lebih menyerupai pemakaian aplikasi asli sekaligus menurunkan frekuensi CAPTCHA yang terpicu.


Routing jaringan yang direkomendasikan per platform

Jenis egress jaringan yang Anda pakai sama pentingnya dengan penyelesaian CAPTCHA itu sendiri. Aplikasi delivery bersifat mobile-first, jadi egress jaringan mobile yang diotorisasi dengan User-Agent iPhone/Android umumnya memberi hasil paling konsisten.

Platform Egress yang disarankan Alasan
DoorDash Mobile (4G) Deteksi bot ketat, mengharapkan traffic mobile
Uber Eats Mobile (4G) Platform mobile-first
Grubhub IP residensial Proteksi standar
Instacart IP residensial Deteksi bot sedang
Just Eat IP residensial rotating Cloudflare standar

Samakan lokasi geografis egress dengan alamat pengiriman yang diuji: biaya antar dan ketersediaan restoran bergantung lokasi, jadi egress dari kota yang salah mengembalikan data harga yang keliru.


Titik data yang layak dilacak

Metrik Nilai bisnis
Harga item menu Paritas harga dan analisis markup
Biaya pengiriman Perbandingan biaya antar platform
Minimum order Analisis hambatan akses
Estimasi waktu antar Perbandingan tingkat layanan
Promosi/diskon Intelijen pemasaran
Ketersediaan restoran Analisis cakupan

Model biaya: thread vs bayar-per-solve

Untuk tim di Indonesia yang sensitif terhadap biaya, model penagihan sering menjadi penentu. CaptchaAI menagih per thread konkuren dengan solve tak terbatas per thread selama sebulan — bukan per CAPTCHA. Saat memantau lima atau enam platform sekaligus, biaya Anda tetap terprediksi berapa pun banyaknya menu yang di-scrape.

Sebagai gambaran, paket BASIC ($15/bulan, 5 thread) cukup untuk tracker skala kecil yang berjalan berkala, sedangkan STANDARD ($30/bulan, 15 thread) memberi ruang konkurensi lebih besar untuk sprint riset harian di banyak kota. Karena tidak ada biaya per solve maupun surcharge per tipe CAPTCHA, menaikkan volume tidak otomatis menaikkan tagihan — Anda tinggal menyesuaikan jumlah thread. Semua harga dalam USD; cek paket terbaru di halaman pricing CaptchaAI.


Masalah umum dan solusinya

Masalah Penyebab Solusi
Hasil restoran kosong Lokasi tidak valid atau halaman terkunci CAPTCHA Set zip/alamat pengiriman yang benar
Harga menu beda dengan aplikasi Selisih harga web vs aplikasi Pakai User-Agent mobile agar setara harga aplikasi
Cloudflare challenge berulang Sinyal browser tidak cocok Pakai egress jaringan mobile yang diotorisasi + User-Agent mobile
Restoran ada di satu platform, tidak di lainnya Cakupan berbeda Tandai sebagai "tidak tersedia" dalam perbandingan
Biaya antar tidak akurat Harga bergantung lokasi Samakan geo egress dengan lokasi target
Token CAPTCHA ditolak berulang sitekey salah diambil atau URL halaman tidak cocok Pastikan data-sitekey dan pageurl sesuai halaman yang dirender

Catatan kepatuhan data

Batasi scraping pada data yang memang boleh Anda proses: utamakan data harga yang bersifat publik dan hindari data pribadi pengguna. Di Indonesia, UU Pelindungan Data Pribadi (UU 27/2022) dan UU ITE menjadikan prinsip ini bukan sekadar etika, melainkan kepatuhan — walau artikel ini bukan nasihat hukum.


Pertanyaan umum

Apakah CaptchaAI bisa menyelesaikan CAPTCHA yang dipakai platform food delivery?

Ya. reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, dan Cloudflare Challenge — tipe yang paling sering dipasang platform delivery — semuanya didukung penuh. Satu API key menangani keempatnya, jadi Anda tidak perlu berpindah layanan antar platform.

Berapa thread yang saya butuhkan untuk memantau beberapa platform sekaligus?

Bergantung pada berapa banyak request yang berjalan paralel, bukan berapa banyak menu yang di-scrape. Tracker kecil yang berjalan berkala biasanya cukup dengan paket BASIC (5 thread); untuk sprint harian lintas kota, STANDARD (15 thread) memberi ruang lebih. Karena solve per thread tak terbatas, Anda menambah thread hanya saat butuh konkurensi lebih tinggi.

Apakah boleh melakukan scraping data harga food delivery?

Selama Anda mengakses data harga yang bersifat publik dan menghormati ketentuan yang berlaku, agregasi harga adalah praktik riset pasar yang umum. Hindari mengumpulkan data pribadi pengguna atau data di balik login milik orang lain. Ini bukan nasihat hukum — sesuaikan dengan kebijakan Anda sendiri.

Seberapa sering data harga sebaiknya diperbarui?

Mingguan sudah memadai untuk pemetaan pasar umum. Saat ada periode promo atau riset kompetitif intensif, perbarui harian agar selisih harga dan promosi tertangkap tepat waktu.


Panduan terkait


Bangun pembanding harga food delivery yang stabil — dapatkan API key CaptchaAI Anda dan otomatiskan analisis lintas platform.

Komentar dinonaktifkan untuk artikel ini.