لجمع أسعار القوائم ورسوم التوصيل من عدة تطبيقات في مسار واحد، تحتاج إلى طبقة تتولّى ما تضعه كل منصة من اختبارات CAPTCHA وكشفٍ للروبوتات. هذا الدليل يوضّح كيف تبني أداة مقارنة عبر DoorDash وUber Eats وGrubhub وغيرها، تمرّر كل تحدٍّ من reCAPTCHA أو Cloudflare Turnstile إلى CaptchaAI وتستكمل الطلب تلقائياً — فصفحة CAPTCHA واحدة غير معالَجة تكفي لإفساد دفعة أسعار كاملة.
سيناريو من السوق العربي
تخيّل محلل بيانات يتابع سوق توصيل الطعام في الخليج ومصر، ويقارن الأسعار عبر التطبيقات العالمية إلى جانب لاعبين محليين مثل Talabat وHungerStation. جميعها تعتمد طبقات متشابهة من reCAPTCHA وCloudflare، فينطبق نمط التكامل نفسه على أي منصة. والفوترة في CaptchaAI بالدولار الأمريكي، ما يبسّط تقدير التكلفة لأي فريق.
ومهما تعددت المنصات، يقوم المسار الموحّد على أربع خطوات متكررة:
- ابحث عن المطعم واجلب صفحة قائمته على كل منصة.
- اكتشف نوع التحقق الظاهر — reCAPTCHA أو Cloudflare Turnstile.
- أرسل التحدي إلى CaptchaAI واستقبل الرمز المحلول.
- أعد إرسال الطلب بالرمز ثم استخرج الأسعار والرسوم للمقارنة.
البيانات التي تستحق التتبّع
قبل كتابة أي سكربت، حدّد ما تريد قياسه؛ فالقيمة التجارية للمقارنة تأتي من مؤشرات بعينها:
اجمع البيانات المتاحة علناً دون تسجيل دخول فقط، والتزم بشروط استخدام كل منصة عند ضبط وتيرة الطلبات.
| المؤشر | القيمة للأعمال |
|---|---|
| أسعار عناصر القائمة | تحليل تكافؤ الأسعار وهوامش الربح |
| رسوم التوصيل | مقارنة رسوم المنصات |
| الحد الأدنى لقيمة الطلب | تحليل حواجز الوصول |
| تقديرات وقت التوصيل | مقارنة مستوى الخدمة |
| العروض والخصومات | معلومات تسويقية تنافسية |
| توافر المطاعم | تحليل التغطية الجغرافية |
أنواع CAPTCHA على منصات التوصيل
تختلف طبقة الحماية من منصة إلى أخرى، ومعرفة النوع المتوقّع مسبقاً تحدّد طريقة إرسال كل تحدٍّ:
| المنصة | نوع التحقق | المُحفِّز | البيانات المحمية |
|---|---|---|---|
| DoorDash | reCAPTCHA v3 + Cloudflare | كشف الروبوتات | القوائم والأسعار والرسوم |
| Uber Eats | Cloudflare Turnstile | الوصول الآلي | قوائم المطاعم والأسعار |
| Grubhub | reCAPTCHA v2 | تحديد معدل الطلبات | عناصر القائمة والعروض |
| Postmates | Cloudflare Challenge | كشف جمع البيانات | رسوم التوصيل وأوقات الوصول |
| Just Eat | reCAPTCHA v2 | عمليات البحث المتكررة | بيانات المطاعم |
| Instacart | reCAPTCHA v3 | كشف الروبوتات | أسعار البقالة |
هذه الأنواع الأربعة كلّها ضمن ما تحلّه CaptchaAI مباشرة، فمسار واحد يكفي لتغطية المنصات الست.
أداة المقارنة عبر المنصات
يبني الكود التالي فئة FoodDeliveryComparator تبحث عن المطاعم وتجلب القوائم وتقارن المطعم نفسه عبر عدة منصات. وعند ظهور CAPTCHA يستخرج مفتاح الموقع، يرسله إلى CaptchaAI بالطريقة المناسبة، ثم يعيد الطلب مع الرمز المحلول:
محدِّدات العناصر في المثال توضيحية؛ استبدلها بمحدِّدات DOM الفعلية لكل منصة قبل التشغيل.
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')}")
اختيار الخادم الوسيط المناسب
تطبيقات التوصيل تعتمد الجوال أولاً، لذا يمنح وكيل الجوال مع User-Agent للجوال أفضل النتائج على المنصات الأشدّ تشدّداً، بينما يكفي وكيل سكني للحماية القياسية. ويتوقّف الاختيار على ثلاثة عوامل:
- صرامة كشف الروبوتات على المنصة المستهدفة.
- مدى اعتماد المنصة على الجوال أولاً.
- معدل الطلبات المتوقّع ومدى الحاجة إلى تدوير العناوين.
| المنصة | الخادم الوسيط الأنسب | السبب |
|---|---|---|
| DoorDash | وكيل الجوال (4G) | كشف مكثّف للروبوتات وتوقّع لحركة الجوال |
| Uber Eats | وكيل الجوال (4G) | منصة تعتمد الجوال أولاً |
| Grubhub | وكيل سكني | حماية قياسية |
| Instacart | وكيل سكني | كشف روبوتات معتدل |
| Just Eat | وكيل سكني دوّار | حماية Cloudflare قياسية |
حل المشكلات الشائعة
معظم الأعطال تعود إلى عدم تطابق الموقع أو البصمة لا إلى الكود:
| المشكلة | السبب | الإجراء |
|---|---|---|
| نتائج مطاعم فارغة | الموقع غير مخدوم أو ظهرت صفحة CAPTCHA | اضبط الرمز البريدي لعنوان التوصيل الصحيح |
| أسعار القائمة تختلف عن التطبيق | اختلاف تسعير الويب عن التطبيق | استخدم User-Agent للجوال للحصول على أسعار مكافئة للتطبيق |
| تكرار حلقة تحدّي Cloudflare | عدم تطابق البصمة | استخدم وكيل جوال مع User-Agent للجوال |
| مطعم يظهر على منصة دون أخرى | اختلاف التغطية | ضَع علامة «غير متوفر» في المقارنة |
| رسوم توصيل غير صحيحة | تسعير يعتمد على الموقع | طابِق الموقع الجغرافي للوكيل مع الموقع المستهدف |
الأسئلة الشائعة
هل يحلّ CaptchaAI أنواع الحماية الموجودة على تطبيقات التوصيل؟
نعم. تظهر على هذه المنصات في الغالب reCAPTCHA v2 وreCAPTCHA v3 وCloudflare Turnstile وCloudflare Challenge، وكلّها ضمن الأنواع التي تحلّها CaptchaAI مباشرة. أما hCaptcha وFunCaptcha فغير مدعومين حالياً، لذا خطّط لمسارك بناءً على الأنواع الفعلية التي تصادفها.
كم تكلّف أتمتة المقارنة عبر عدة منصات؟
تعتمد فوترة CaptchaAI على عدد الـ Threads المتزامنة لا على عدد عمليات الحل، مع حلول غير محدودة لكل Thread. تبدأ خطة BASIC من 15 دولاراً شهرياً مقابل 5 Threads، وSTANDARD من 30 دولاراً مقابل 15 Thread، وADVANCE من 90 دولاراً مقابل 50 Thread — وكلما زادت المنصات التي تجمعها بالتوازي اخترت عدد Threads أعلى.
لماذا تختلف الأسعار بين منصات التوصيل نفسها؟
تضبط المطاعم أسعاراً مختلفة لكل منصة لتعويض معدلات العمولة المتفاوتة (15–30%)، كما تختلف رسوم التوصيل والخدمة من منصة لأخرى، وهو ما يجعل المقارنة المنتظمة ذات قيمة.
كيف أتعامل مع تكرار حلقة تحدّي Cloudflare؟
السبب غالباً عدم اتساق البصمة بين الوكيل وUser-Agent. استخدم وكيل جوال مع User-Agent للجوال، وحافظ على ثبات الجلسة، وتجنّب رفع معدل الطلبات دفعة واحدة.
أدلة ذات صلة
- ابدأ مع CaptchaAI: حلّ أول اختبار CAPTCHA خلال 5 دقائق
- أتمتة مراقبة حالة الرحلات مع حل اختبار CAPTCHA
قارن أسعار توصيل الطعام على نطاق واسع — احصل على مفتاح CaptchaAI وأتمِت التحليل عبر المنصات.