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:
- Deteksi apakah halaman memuat CAPTCHA (cari penanda
data-sitekey,g-recaptcha, ataucf-turnstile). - Kirim task ke endpoint
in.phpbeserta sitekey dan URL halaman, lalu simpan task ID yang dikembalikan. - Lakukan polling ke
res.phpsampai token siap. reCAPTCHA v3 biasanya selesai dalam hitungan detik, Cloudflare Turnstile umumnya di bawah 10 detik, sedangkan reCAPTCHA v2 bisa memakan waktu lebih lama. - Sisipkan token yang didapat (
g-recaptcha-responseuntuk reCAPTCHA,cf-turnstile-responseuntuk 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
- Pemantauan inventaris retail dengan penanganan CAPTCHA
- Egress jaringan mobile untuk menaikkan keberhasilan solve CAPTCHA
- IP residensial bergilir untuk penyelesaian CAPTCHA
Bangun pembanding harga food delivery yang stabil — dapatkan API key CaptchaAI Anda dan otomatiskan analisis lintas platform.