يتوقف أي سكربت لأتمتة النماذج عند أول اختبار CAPTCHA يعترض الصفحة، وهنا يضيع وقت فرق ضمان الجودة والمطورين. الحل مباشر ويتكوّن من ثلاث حركات فقط: اكتشف نوع الاختبار الموجود في الصفحة برمجياً، أرسله إلى CaptchaAI ليعيد لك رمز الحل، ثم احقن هذا الرمز في النموذج قبل الضغط على زر الإرسال.
- الكشف — حدّد ما إذا كانت الصفحة تستخدم reCAPTCHA v2 أو Cloudflare Turnstile أو اختبار صورة.
- الحل — أرسل بيانات الاختبار إلى CaptchaAI واستقبل الرمز الجاهز للحقن.
- الحقن والإرسال — ضع الرمز في الحقل المناسب وأرسل النموذج فوراً قبل انتهاء صلاحيته.
في هذا الدليل نبني هذا المسار خطوة بخطوة باستخدام Selenium وPython، ونغطّي reCAPTCHA v2 وCloudflare Turnstile واختبارات الصور ضمن سير عمل واحد قابل للتكرار على النماذج التي تملكها أو المصرّح لك باختبارها.
لماذا يعيق CAPTCHA أتمتة النماذج
صُمّم اختبار CAPTCHA أساساً لمنع الإرسال الآلي، لذا فهو يوقف أي أداة تحاول تعبئة نموذج وإرساله دون تدخل بشري. تظهر هذه العقبة في مواقف يومية كثيرة أمام فرق التطوير في المنطقة العربية:
- اختبار نماذج التواصل والتسجيل ضمن دورات ضمان الجودة قبل كل إصدار.
- التحقق الدوري من أن نموذج الدفع أو الاشتراك ما زال يعمل بعد كل تحديث.
- أتمتة تقديم الطلبات على بوابة خدمية تملك عليها حساباً ومصرّحاً لك باختبارها.
في كل هذه الحالات لا يقبل النموذج الإرسال حتى يُحلّ الاختبار، ولهذا نحتاج إلى طبقة برمجية تكتشف الاختبار وتحلّه تلقائياً قبل الوصول إلى زر الإرسال.
بنية سير العمل
يمرّ النموذج بأربع مراحل متتابعة: تحميل الصفحة عبر Selenium، ثم تعبئة الحقول، ثم اكتشاف الاختبار وحلّه، وأخيراً الإرسال. يلخّص المخطط التالي هذا التدفّق:
┌────────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────────┐
│ Load Form │────▶│ Fill Fields │────▶│ Detect & │────▶│ Submit Form │
│ (Selenium) │ │ │ │ Solve │ │ │
│ │ │ │ │ CAPTCHA │ │ │
└────────────┘ └──────────────┘ └────────────┘ └──────────────┘
المكونات الأساسية
نقسّم الكود إلى ثلاث مسؤوليات واضحة: فئة تتخاطب مع CaptchaAI، وفئة تكتشف نوع الاختبار في الصفحة، وفئة تدير المتصفح وتربط كل شيء معاً. هذا الفصل يجعل كل جزء قابلاً للاختبار والصيانة على حدة.
فئة حلّ CAPTCHA
تتولى هذه الفئة إرسال بيانات الاختبار إلى نقطة النهاية in.php، ثم استطلاع النتيجة دورياً عبر res.php حتى يصبح الرمز جاهزاً. لاحظ مهلة الانتظار الأولية والفاصل بين محاولات الفحص الدوري؛ فهما يوازنان بين سرعة الاستجابة وعدم إغراق الخدمة بالطلبات:
import time
import requests
class FormCaptchaSolver:
BASE = "https://ocr.captchaai.com"
def __init__(self, api_key):
self.api_key = api_key
def solve(self, params, initial_wait=10):
params["key"] = self.api_key
params["json"] = 1
resp = requests.post(f"{self.BASE}/in.php", data=params).json()
if resp["status"] != 1:
raise Exception(f"Submit error: {resp['request']}")
task_id = resp["request"]
time.sleep(initial_wait)
for _ in range(60):
result = requests.get(
f"{self.BASE}/res.php",
params={"key": self.api_key, "action": "get", "id": task_id, "json": 1},
).json()
if result["request"] == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result["status"] == 1:
return result["request"]
raise Exception(f"Solve error: {result['request']}")
raise TimeoutError("CAPTCHA solve timed out")
كاشف نوع الاختبار
قبل طلب الحل نحتاج إلى معرفة نوع الاختبار الموجود فعلاً في الصفحة. تفحص هذه الفئة شيفرة الصفحة بحثاً عن سمات مميّزة: الصنف cf-turnstile لـ Turnstile، والسمة data-sitekey المقترنة بإشارة reCAPTCHA، أو وسم صورة يحمل كلمة captcha في مصدره. ترتيب الفحص مهم لأن بعض الصفحات تحتوي أكثر من عنصر في وقت واحد:
import re
from selenium.webdriver.common.by import By
class CaptchaDetector:
def __init__(self, driver):
self.driver = driver
def detect(self):
"""Detect CAPTCHA type on current page."""
html = self.driver.page_source
# Turnstile
turnstile = self.driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile, [data-sitekey]")
for el in turnstile:
if "cf-turnstile" in (el.get_attribute("class") or ""):
return "turnstile", el.get_attribute("data-sitekey")
# reCAPTCHA
recaptcha = self.driver.find_elements(By.CSS_SELECTOR, "[data-sitekey]")
if recaptcha:
sitekey = recaptcha[0].get_attribute("data-sitekey")
if "recaptcha" in html.lower():
return "recaptcha_v2", sitekey
# Image CAPTCHA
img = self.driver.find_elements(By.CSS_SELECTOR, "img[src*='captcha'], img.captcha")
if img:
return "image", img[0].get_attribute("src")
return "none", None
منسّق أتمتة النموذج
تجمع فئة FormAutomator كل شيء: تفتح المتصفح، وتملأ الحقول، وتستدعي الكاشف لتحديد النوع، ثم تحقن الرمز في الحقل الصحيح لكل حالة — g-recaptcha-response مع reCAPTCHA، وcf-turnstile-response مع Turnstile، وحقل نصي مباشر مع اختبار الصورة. وفي النهاية تضغط زر الإرسال وتعيد عنوان الصفحة الناتجة:
import base64
import requests as req
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class FormAutomator:
def __init__(self, api_key):
self.solver = FormCaptchaSolver(api_key)
self.driver = webdriver.Chrome()
self.detector = CaptchaDetector(self.driver)
def fill_field(self, selector, value):
field = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, selector))
)
field.clear()
field.send_keys(value)
def select_option(self, selector, value):
from selenium.webdriver.support.ui import Select
select = Select(self.driver.find_element(By.CSS_SELECTOR, selector))
select.select_by_value(value)
def solve_captcha(self):
captcha_type, data = self.detector.detect()
page_url = self.driver.current_url
if captcha_type == "recaptcha_v2":
token = self.solver.solve({
"method": "userrecaptcha",
"googlekey": data,
"pageurl": page_url,
})
self.driver.execute_script(
f'document.querySelector("[name=g-recaptcha-response]").value = "{token}";'
)
return True
if captcha_type == "turnstile":
token = self.solver.solve({
"method": "turnstile",
"sitekey": data,
"pageurl": page_url,
})
self.driver.execute_script(
f'document.querySelector("[name=cf-turnstile-response]").value = "{token}";'
)
return True
if captcha_type == "image":
img_data = req.get(data).content
img_b64 = base64.b64encode(img_data).decode()
text = self.solver.solve({"method": "base64", "body": img_b64})
captcha_input = self.driver.find_element(
By.CSS_SELECTOR, "input[name*='captcha']"
)
captcha_input.clear()
captcha_input.send_keys(text)
return True
return False # No CAPTCHA detected
def submit_form(self, url, fields, submit_selector="button[type='submit']"):
"""
fields: list of (selector, value) tuples
"""
self.driver.get(url)
for selector, value in fields:
self.fill_field(selector, value)
self.solve_captcha()
submit = self.driver.find_element(By.CSS_SELECTOR, submit_selector)
submit.click()
return self.driver.current_url
def close(self):
self.driver.quit()
مثال عملي: نموذج تواصل
تخيّل فريق ضمان جودة في متجر إلكتروني بالقاهرة يريد التأكد ليلياً من أن نموذج «تواصل معنا» على موقعه ما زال يعمل بعد آخر تحديث. النموذج محمي بـ reCAPTCHA v2، والفريق يملك الموقع ومصرّح له باختباره. الكود التالي يعبّئ الحقول، يحل الاختبار، ثم يرسل النموذج ويطبع عنوان صفحة التأكيد:
automator = FormAutomator("YOUR_API_KEY")
try:
result_url = automator.submit_form(
url="https://example.com/contact",
fields=[
("#name", "John Doe"),
("#email", "john@example.com"),
("#subject", "Sales inquiry"),
("#message", "I'd like to learn more about your services."),
],
submit_selector="#submit-btn",
)
print(f"Form submitted. Redirected to: {result_url}")
finally:
automator.close()
التعامل مع أنواع نماذج مختلفة
الفئة نفسها تخدم أي نموذج تقريباً؛ كل ما يتغيّر هو قائمة الحقول ومحدّد زر الإرسال. فيما يلي ثلاثة أنماط شائعة تلتقيها في أغلب المشاريع:
نموذج تسجيل الدخول
result = automator.submit_form(
url="https://example.com/login",
fields=[
("#username", "testuser"),
("#password", "testpass123"),
],
submit_selector="#login-btn",
)
استمارة التسجيل
result = automator.submit_form(
url="https://example.com/register",
fields=[
("#first-name", "Jane"),
("#last-name", "Smith"),
("#email", "jane@example.com"),
("#password", "SecurePass!123"),
("#confirm-password", "SecurePass!123"),
],
submit_selector="#register-btn",
)
نموذج بحث محمي بـ CAPTCHA
result = automator.submit_form(
url="https://example.com/search",
fields=[
("#query", "python developer"),
("#location", "San Francisco"),
],
submit_selector="#search-btn",
)
أنواع CAPTCHA التي يغطّيها هذا المسار
يتعامل الكاشف في هذا الدليل مع ثلاثة أنواع، وكلها مدعومة رسمياً في CaptchaAI ضمن قائمة أوسع من الأنواع:
- reCAPTCHA v2 بنسختيه المرئية وغير المرئية، وreCAPTCHA v3.
- Cloudflare Turnstile وCloudflare Challenge.
- اختبارات الصور والنصوص (OCR) والشبكة الصورية، إضافة إلى GeeTest v3.
أما hCaptcha وFunCaptcha (Arkose Labs) فغير مدعومة حالياً، وGeeTest v4 لا يزال «قيد التطوير» ولم يُتَح بعد؛ لذا لا يشملها هذا المسار. وإذا التقى الكاشف نوعاً غير مدعوم، فالأفضل تسجيل الحالة والتوقف بدل محاولة إرسال رمز لن يُقبل.
نصائح لموثوقية الإرسال
تنجح أتمتة النماذج أو تتعثّر بحسب التفاصيل الصغيرة. هذه ممارسات تجعل المسار أكثر استقراراً على المدى الطويل:
- حلّ الاختبار في آخر لحظة — لرموز reCAPTCHA وTurnstile صلاحية قصيرة، فاجعل الحل آخر خطوة قبل الإرسال مباشرة لتفادي انتهاء الصلاحية.
- استخدم انتظاراً صريحاً — انتظر ظهور كل حقل عبر WebDriverWait بدل الاعتماد على مهلة ثابتة، خاصة مع الصفحات التي تحمّل عناصرها ديناميكياً.
- أعد المحاولة بذكاء — عند رفض الرمز أو فشل التحقق من جانب الخادم، أعد الكشف والحل بدل إعادة إرسال الرمز القديم نفسه.
- اختبر ما تملكه فقط — شغّل هذه الأتمتة على نماذجك أو على بيئات مصرّح لك بها، ضمن إطار اختبار واضح المراقبة.
استكشاف الأخطاء وإصلاحها
عند تعطّل المسار يعود السبب غالباً إلى واحدة من الحالات التالية، ولكل منها إجراء مباشر:
| المشكلة | السبب المحتمل | الإجراء |
|---|---|---|
| رُفض الرمز | انتهت صلاحيته قبل الإرسال | اجعل حل CAPTCHA آخر خطوة ثم أرسل فوراً |
| لم يُعثر على الحقل | تحميل ديناميكي متأخر للعناصر | أضف انتظاراً صريحاً قبل تعبئة الحقل |
| اكتُشف نوع خاطئ | تعدّد عناصر CAPTCHA في الصفحة | راجع ترتيب منطق الكشف |
| النموذج يعيد التحميل بعد الإرسال | فشل التحقق من جانب الخادم | تأكّد من جميع الحقول المطلوبة |
| لم يُستدعَ رد نداء reCAPTCHA | يلزم استدعاء دالة رد النداء | استخدم grecaptcha.execute() بعد الحقن |
الأسئلة الشائعة
ما أنواع CAPTCHA التي يمكن لهذا المسار حلّها؟
reCAPTCHA v2 بنسختيه المرئية وغير المرئية وv3، وCloudflare Turnstile وChallenge، واختبارات الصور والنصوص وGeeTest v3. أما hCaptcha وFunCaptcha فغير مدعومين حالياً، لذا يخرجان عن نطاق هذا الكاشف.
كم تبلغ تكلفة تشغيل هذا على نطاق واسع؟
يعتمد CaptchaAI تسعيراً قائماً على عدد الخيوط المتزامنة بدل الدفع لكل عملية حل. تبدأ الباقات من BASIC عند 15 دولاراً شهرياً مقابل 5 خيوط مع عدد غير محدود من عمليات الحل خلال الشهر، ويعالج كل خيط اختباراً واحداً في الوقت نفسه، فترفع عدد الخيوط كلما زاد التوازي المطلوب.
كيف أتجنّب رفض الرمز بسبب انتهاء صلاحيته؟
اجعل حل الاختبار آخر خطوة قبل الإرسال مباشرة. فإذا مرّ وقت طويل بين الحل والإرسال فقد تنتهي صلاحية الرمز ويرفضه الخادم؛ في هذه الحالة أعد الكشف والحل ثم أرسل النموذج فوراً.
هل يمكنني تشغيله بدون واجهة رسومية على خادم؟
نعم، شغّل Chrome في الوضع الخفي headless على الخادم؛ تعمل الفئات نفسها دون أي تغيير. تأكّد فقط من تثبيت المتصفح ومشغّله المناسب داخل بيئة التشغيل.
أدلة ذات صلة
لتوسيع هذا المسار نحو أحمال أكبر أو سيناريوهات مراقبة مستمرة:
جاهز لأتمتة نماذجك؟ ابدأ حلّ CAPTCHA مع CaptchaAI واحصل على أول رمز محلول خلال دقائق.