أربع خطوات تفصل سكربت جمع متوقف عن آخر يكمل دورته: ارصد التحويل إلى google.com/sorry/، استخرج مفتاح الموقع من صفحة التحدي، أرسل المهمة إلى CaptchaAI للحصول على الرمز، ثم أعد إرسال النموذج بالرمز وتابع من نقطة التوقف. ما يسبق ذلك مجرد عَرَض: استجابة 429 بعد عشرات الاستعلامات الناجحة، أو صفحة تحقق تحل محل صفحة النتائج بلا مقدمات.
قبل أي سطر برمجي، حدّد نطاق عملك: هذا المسار لبيانات عامة أو لبيانات تملك تصريحاً بالوصول إليها — ترتيب صفحاتك، ظهور علامتك التجارية، أداء محتواك. أما ما يقع خلف تسجيل دخول لا تملكه فخارج نطاق هذا الدليل.
متى يستحق هذا المسار العناء؟
الحاجة لا تظهر عند الاستعلام العاشر بل عند الألف. ثلاثة سيناريوهات متكررة في السوق العربي:
- وكالة تسويق في دبي تتابع 300 كلمة مفتاحية عربية لعملائها على
google.aeوgoogle.com.sa، وتحتاج لقطة يومية ثابتة التوقيت لمقارنة الترتيب أسبوعياً. - متجر تجزئة سعودي يريد معرفة من يظهر فوقه في نتائج البحث عن أسماء منتجاته قبل موسم العروض، خصوصاً في ساعات الذروة المسائية بتوقيت الخليج.
- فريق محتوى في القاهرة يقيس أثر تحديث خوارزمي على صفحاته العربية، فيحتاج أرشيفاً لصفحات النتائج قبل التحديث وبعده.
القاسم المشترك بين الثلاثة أن الاستعلامات عربية وموجّهة إلى نطاق محلي مع معامل اللغة hl=ar. اجعل منطقة الخادم الوسيط مطابقة للنطاق الذي تستعلمه؛ استعلام عربي قادم من عنوان IP أوروبي يجمع إشارتين متناقضتين ويستدعي التحدي أسرع.
الإشارات التي تدفع Google إلى عرض التحدي
| الإشارة | ما الذي ترصده |
|---|---|
| معدل الاستعلام | عدد كبير من عمليات البحث من عنوان IP واحد خلال وقت قصير |
| سمعة عنوان IP | عناوين مراكز البيانات أو خوادم وسيطة سبق وضع علامة عليها |
| غياب ملفات تعريف الارتباط | جلسة بلا ملفات تعريف ارتباط سابقة من Google |
| النمط السلوكي | استعلامات متطابقة البنية بلا فواصل زمنية أو وقت قراءة |
| بصمة JavaScript | غياب مؤشرات بيئة المتصفح المتوقعة |
النتيجة العملية لأي من هذه الإشارات واحدة: استجابة 429 Too Many Requests، أو تحويل إلى صفحة تحدٍ على المسار google.com/sorry/. وجود التحدي ليس عقوبة نهائية — إنه طلب تحقق يمكن إتمامه ثم متابعة الجلسة.
ما تحتاجه قبل تشغيل أول استعلام
- مفتاح CaptchaAI API من captchaai.com، ولوحة التحكم تعرض الرصيد وعدد الـ threads المتاحة.
- Python 3.7 أو أحدث مع مكتبتي requests وBeautifulSoup لتحليل HTML.
- خادم وسيط سكني موزّع جغرافياً؛ عناوين مراكز البيانات تُرصد بسرعة على هذا النوع من الاستعلامات.
- خطة تناسب حجم عملك: BASIC بسعر 15 دولاراً شهرياً تمنحك 5 threads، وSTANDARD بسعر 30 دولاراً تمنحك 15 thread. الفوترة قائمة على عدد المهام المتزامنة لا على عدد الحلول، ولكل thread عدد حلول غير محدود خلال الشهر.
المسار العملي من ظهور التحدي إلى استئناف الجمع
الخطوة 1: ارصد لحظة ظهور التحدي
لا تنتظر أن يفشل التحليل. افحص عنوان الاستجابة النهائي ورمز الحالة بعد كل طلب؛ وجود sorry في المسار أو الرمز 429 يعني أن ما بين يديك صفحة تحدٍ لا صفحة نتائج.
import requests
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept-Language": "en-US,en;q=0.9"
})
resp = session.get("https://www.google.com/search?q=example")
if "sorry" in resp.url or resp.status_code == 429:
print("CAPTCHA triggered!")
captcha_url = resp.url
else:
print("Results loaded")
الخطوة 2: استخرج مفتاح الموقع من صفحة التحدي
مفتاح الموقع موجود في السمة data-sitekey داخل عنصر reCAPTCHA. سجّل حالة عدم العثور عليه بدل تجاهلها؛ فهي غالباً مؤشر على تغيّر في بنية صفحة التحدي.
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, "html.parser")
# Google uses data-sitekey on the reCAPTCHA div
recaptcha = soup.find("div", {"data-sitekey": True})
if recaptcha:
site_key = recaptcha["data-sitekey"]
print(f"Site key: {site_key}")
الخطوة 3: أرسل المهمة إلى CaptchaAI واستطلع النتيجة
الإرسال إلى in.php يعيد معرّف المهمة، ثم تستطلع res.php كل خمس ثوانٍ حتى يصلك الرمز. اضبط توقعاتك الزمنية على أساس النوع: reCAPTCHA v2 يُحل عادة في أقل من 60 ثانية، وreCAPTCHA v3 في أقل من 4 ثوانٍ، مع معدل نجاح مرتفع على الأنواع المدعومة.
import time
API_KEY = "YOUR_API_KEY"
def solve_google_recaptcha(site_key, page_url):
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url
})
if not resp.text.startswith("OK|"):
raise Exception(f"Submit error: {resp.text}")
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id
})
if result.text == "CAPCHA_NOT_READY":
continue
if result.text.startswith("OK|"):
return result.text.split("|")[1]
raise Exception(f"Error: {result.text}")
raise TimeoutError("Timed out")
token = solve_google_recaptcha(site_key, captcha_url)
الخطوة 4: أعد إرسال النموذج مع الرمز
الرمز وحده لا يكفي. أعد إرسال النموذج كاملاً بحقوله المخفية كما وردت، مع إضافة g-recaptcha-response، ومن الجلسة نفسها حتى تبقى ملفات تعريف الارتباط متسقة.
# Find the form action URL and hidden fields
form = soup.find("form")
form_data = {}
for inp in form.find_all("input", {"name": True}):
form_data[inp["name"]] = inp.get("value", "")
form_data["g-recaptcha-response"] = token
action = form.get("action", "")
if action.startswith("/"):
action = f"https://www.google.com{action}"
result = session.post(action, data=form_data)
print(f"Redirected to: {result.url}")
سكربت كامل يجمع النتائج ويعالج التحدي تلقائياً
الخطوات الأربع السابقة تصبح دالة واحدة: استعلم، وإن ظهر التحدي فعالِجه ثم أعد قراءة الصفحة، وحلّل النتائج في الحالتين بالمنطق نفسه.
import requests
import time
from bs4 import BeautifulSoup
API_KEY = "YOUR_API_KEY"
def solve_captcha(site_key, page_url):
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY, "method": "userrecaptcha",
"googlekey": site_key, "pageurl": page_url
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
r = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id
})
if r.text == "CAPCHA_NOT_READY": continue
if r.text.startswith("OK|"): return r.text.split("|")[1]
raise TimeoutError()
def google_search(query, num_results=10):
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept-Language": "en-US,en;q=0.9"
})
resp = session.get("https://www.google.com/search", params={
"q": query, "num": num_results
})
# Handle CAPTCHA
if "sorry" in resp.url or resp.status_code == 429:
soup = BeautifulSoup(resp.text, "html.parser")
rc = soup.find("div", {"data-sitekey": True})
if rc:
token = solve_captcha(rc["data-sitekey"], resp.url)
form = soup.find("form")
data = {i["name"]: i.get("value", "")
for i in form.find_all("input", {"name": True})}
data["g-recaptcha-response"] = token
action = form.get("action", resp.url)
if action.startswith("/"):
action = f"https://www.google.com{action}"
resp = session.post(action, data=data)
# Parse results
soup = BeautifulSoup(resp.text, "html.parser")
results = []
for div in soup.find_all("div", class_="g"):
link = div.find("a")
title = div.find("h3")
if link and title:
results.append({
"title": title.text,
"url": link.get("href")
})
return results
results = google_search("best captcha solving api")
for r in results:
print(f"{r['title']}: {r['url']}")
في التشغيل الفعلي، أضف تسجيلاً لكل حالة تحدٍ: الوقت والخادم الوسيط والاستعلام. هذا السجل يكشف لاحقاً أي مجموعة عناوين تستهلك رصيدك دون داعٍ.
قواعد ضبط المعدل التي تقلّل ظهور التحدي
- اعتمد الخوادم الوسيطة السكنية — عناوين مراكز البيانات تُحجب على هذا المسار بسرعة كبيرة.
- وزّع التوقيت عشوائياً — فاصل بين 5 و15 ثانية بين الاستعلامات أقرب إلى سلوك بشري من فاصل ثابت.
- نوّع User-Agent — دوّر بين سلاسل متصفحات واقعية وحديثة، لا سلاسل مختلقة.
- اضبط سقفاً للحجم — أبقِ الاستعلامات أقل من 100 استعلام في الساعة لكل عنوان IP.
- طابق النطاق مع المنطقة — استعلام على
google.com.saمن عنوان داخل السعودية أقل إثارة للشك من العكس.
كيف تُحسب التكلفة قبل التوسع
الفوترة في CaptchaAI قائمة على عدد الـ threads المتزامنة، أي عدد مهام التحقق قيد المعالجة في اللحظة نفسها، وليس على عدد الحلول. عملياً هذا يعني أن سؤالك ليس "كم تحدياً سأحل هذا الشهر؟" بل "كم مهمة تحقق ستكون مفتوحة في الوقت نفسه عند ذروة الجمع؟".
خط جمع واحد بمعدل محافظ نادراً ما يفتح أكثر من مهمتين في اللحظة ذاتها، فتكفيه خطة صغيرة. أما تشغيل عدة عمليات جمع متوازية لعدة عملاء في النافذة الزمنية نفسها فيدفعك إلى خطة أعلى — لا لأن عدد الحلول زاد، بل لأن التزامن زاد.
استكشاف الأخطاء وإصلاحها
| العَرَض | الإجراء |
|---|---|
| تحدٍ عند كل طلب | انتقل إلى خوادم وسيطة سكنية وخفّض معدل الاستعلامات |
| مفتاح الموقع غير موجود في الصفحة | تحقق من بنية صفحة التحدي؛ قد تكون تغيّرت |
| الرمز قُبل والحظر مستمر | قد يكون المطلوب تحققاً إضافياً؛ جرّب خادماً وسيطاً آخر وجلسة جديدة |
| صفحة النتائج فارغة | تأكد من عدم تقديم تخطيط بديل للصفحة قبل تعديل محددات التحليل |
حدود يجب الالتزام بها
اقصر الجمع على البيانات العامة أو التي تملك حق الوصول إليها، والتزم بشروط الاستخدام، واحتفظ بسجل لما جُمع ولماذا. الانضباط في الحجم ليس التزاماً أخلاقياً فحسب: خط الجمع الهادئ يبقى مستقراً لأشهر، والعدواني يستهلك الرصيد ويتعطل.
الأسئلة الشائعة
هل ما زلت بحاجة إلى خادم وسيط سكني إذا كنت أستخدم CaptchaAI؟
نعم. الخدمة تحل التحدي بعد ظهوره، لكنها لا تغيّر جودة عنوان IP الذي تستعلم منه. الخادم الوسيط يقلّل عدد مرات ظهور التحدي، وCaptchaAI يتكفل بالمرات التي تحدث فعلاً.
الرمز قُبل لكن الحظر مستمر — أين الخطأ؟
غالباً في الجلسة لا في الرمز. تأكد من إعادة الإرسال من الجلسة نفسها التي استقبلت صفحة التحدي، ومن أن كل الحقول المخفية أُعيد إرسالها كما وردت، ومن أن الخادم الوسيط لم يتغيّر بين الطلب والاستجابة.
ماذا لو اعتمد محرك بحث آخر على اختبار hCaptcha؟
لا، لا يشمل الدعم الحالي hCaptcha ولا FunCaptcha. المدعوم هنا هو عائلة reCAPTCHA بإصداراتها، إضافة إلى Cloudflare Turnstile وCloudflare Challenge وGeeTest v3 واختبارات الصور والشبكة، بينما GeeTest v4 ما زال ضمن خطة "قريباً" وليس متاحاً.
هل أشغّل الجمع من خادم داخل المنطقة أم من مركز بيانات أوروبي؟
شغّل الاستعلامات العربية من عناوين قريبة جغرافياً من النطاق المستهدف. اللغة والنطاق والمنطقة الزمنية إشارات مترابطة، وتناقضها أسرع طريق إلى صفحة التحقق. إن كان الخادم أوروبياً، فليكن الخروج عبر خادم وسيط في المنطقة المستهدفة.
كم استعلاماً يمكنني تنفيذه قبل ظهور التحدي؟
لا يوجد رقم ثابت؛ يعتمد على جودة العنوان ونمط الطلبات وتوقيتها. مع خوادم وسيطة سكنية وفواصل متغيرة يمكن غالباً تنفيذ عشرات الاستعلامات قبل أول تحدٍ، بينما يظهر مبكراً جداً عند العمل بلا خادم وسيط.