تفشل معظم محاولات حل Grid Image CAPTCHA لسبب واحد من أربعة: التقاط صورة رديء، تنسيق إرسال غير صالح، صورة لا يستطيع المحلل قراءتها، أو تطبيق خاطئ للإحداثيات على الصفحة. حين تعرف في أي مرحلة يقع الخطأ يصبح إصلاحه مباشراً. يمرّ كل تحدٍّ شبكي بأربع مراحل، والخطأ يقع في واحدة منها فقط:
- الإرسال: ترميز الصورة ونقلها إلى الـ API.
- التعرّف: قراءة المحلل للصورة وتحديد الكائنات المطلوبة.
- التطبيق: إسقاط الإحداثيات على خلايا الصفحة وتنفيذ النقر.
- المهلة: بقاء التحدي صالحاً إلى أن تعود النتيجة.
مثال من الواقع: فريق في القاهرة يبني اختبار جودة (QA) آلياً لبوابة حجز مواعيد تستخدم تحدي صور شبكية من نوع BLS، وكانت معظم طلباته ترجع ERROR_ZERO_CAPTCHA_FILESIZE. تبيّن أن لقطة الشاشة كانت تُلتقط قبل اكتمال تحميل الصورة؛ وبعد إضافة انتظار صريح لاكتمال العنصر اختفى الخطأ تماماً. أغلب مشكلات الشبكة تُحلّ بالمنطق نفسه: حدِّد المرحلة أولاً، ثم عالج سببها. استعن بالجدول التالي لتحديد نقطة البدء حسب العرَض الذي تراه:
| العرَض الذي تراه | المرحلة المرجّحة | ابدأ من قسم |
|---|---|---|
| رفض فوري للطلب أو خطأ حجم/تنسيق | الإرسال | أخطاء إرسال الصورة إلى الـ API |
| نتيجة «يتعذّر الحل» أو خلايا خاطئة | التعرّف | فشل التعرّف على الصورة |
| النقر لا يُطبَّق أو يقع على الخلية المجاورة | التطبيق | أخطاء عند تطبيق الحل على الصفحة |
| انتهاء صلاحية التحدي أو حلقة انتظار لا تنتهي | المهلة | أخطاء انتهاء المهلة |
أخطاء إرسال الصورة إلى الـ API
ERROR_WRONG_FILE_EXTENSION
العرَض: يُرفض الطلب فور إرساله ولا تصل مهمة حل إلى الخدمة أصلاً.
السبب: الملف المُرسَل ليس بتنسيق صورة صالح، أو أنه ما زال يحمل بادئة data URI لم تُزَل.
الإصلاح:
- أرسل الصورة بتنسيق PNG أو JPEG فقط
- تأكد أن سلسلة base64 مُرمَّزة ترميزاً سليماً وغير مقطوعة
- احذف البادئة
data:image/...;base64,قبل الإرسال؛ فالـ API يتوقّع بيانات base64 خام
# WRONG — includes data URI prefix
body = "data:image/png;base64,iVBORw0KGgo..."
# CORRECT — raw base64 only
body = "iVBORw0KGgo..."
ERROR_TOO_BIG_CAPTCHA_FILESIZE
العرَض: الخدمة ترفض الصورة قبل أن تبدأ الحل بسبب كِبَر حجمها.
السبب: تجاوز حجم الصورة الحدَّ الأقصى المسموح (600 كيلوبايت عادةً).
الإصلاح: صغِّر أبعاد الصورة قبل ترميزها مع الحفاظ على وضوح حدود الخلايا:
from PIL import Image
import io
import base64
# Resize if too large
img = Image.open("captcha.png")
if img.width > 600:
ratio = 600 / img.width
img = img.resize((600, int(img.height * ratio)), Image.LANCZOS)
buffer = io.BytesIO()
img.save(buffer, format="PNG")
b64 = base64.b64encode(buffer.getvalue()).decode()
نصيحة: قلّل الأبعاد لا الجودة؛ فضغط JPEG العنيف قد يُذيب حدود الخلايا ويرفع نسبة أخطاء التعرّف بدل أن يعالج الحجم.
ERROR_ZERO_CAPTCHA_FILESIZE
العرَض: الطلب يمرّ لكن الصورة المرسَلة فارغة أو حجمها صفر.
السبب: الملف فارغ، أو فشل استخراج الصورة من الصفحة قبل التقاطها.
الإصلاح:
- تأكد من اكتمال تحميل عنصر الصورة قبل استخراجه
- تحقق أن السمة
srcليست فارغة ولا تحمل قيمة مؤقتة (placeholder) - انتظر الصور المُحمَّلة بأسلوب lazy-load قبل الالتقاط
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Wait for image to load
WebDriverWait(driver, 10).until(
lambda d: d.find_element(By.CSS_SELECTOR, ".captcha img").get_attribute("complete") == "true"
)
فشل التعرّف على الصورة (أخطاء الحل)
ERROR_CAPTCHA_UNSOLVABLE
العرَض: المهمة تُقبل ثم تعود بنتيجة «يتعذّر الحل» بعد المعالجة.
السبب: الصورة ضبابية أو مشوَّهة، أو أن الكائنات داخلها غير واضحة بما يكفي للتعرّف عليها.
الإصلاح:
- التقط الصورة بدقتها الكاملة ولا تُصغِّرها
- تأكد أن لا تراكبات (overlays) أو علامات مائية تحجب الشبكة
- أعد المحاولة بتحدٍّ جديد؛ فبعض التحديات غامضة بطبيعتها حتى للعين البشرية
خلايا محدَّدة بشكل خاطئ
العرَض: يعود حل صالح لكنه يشير إلى خلايا غير التي تحتوي الكائن المطلوب.
السبب: جودة صورة منخفضة أو التقاط جزئي للشبكة.
الإصلاح:
- التقط عنصر الكابتشا كاملاً بما في ذلك حدوده
- لا تقصّ الصورة بإحكام شديد؛ اترك بضع بكسلات هامشاً
- تحقق يدوياً بحفظ الصورة الملتقطة ومراجعتها قبل اعتماد الحل
# Take a proper element screenshot
captcha_el = driver.find_element(By.CSS_SELECTOR, "#captcha-container")
captcha_el.screenshot("debug_captcha.png")
# Open and check manually
from PIL import Image
Image.open("debug_captcha.png").show()
أخطاء عند تطبيق الحل على الصفحة
خطأ الإزاحة بمقدار واحد — off-by-one
العرَض: الحل صحيح لكن النقر يقع دائماً على الخلية المجاورة للصحيحة.
السبب: عدم تطابق بين استجابة الـ API المبنية على الفهرسة من 1 وفهرسة المصفوفات المبنية على 0.
# API returns "1,3,5" (1-based)
solution = "1,3,5"
indices = [int(i) for i in solution.split(",")]
# DON'T: use directly as array index
# cells[1], cells[3], cells[5] ← WRONG (off by one)
# DO: convert to 0-based
for idx in indices:
cells[idx - 1].click() # 1→0, 3→2, 5→4
الخلايا لا تستجيب للنقر
العرَض: النقر يُنفَّذ برمجياً لكن الخلية لا تتغيّر ولا يُقبل التحدي.
السبب: هدف النقر غير صحيح — تراكب، أو iframe، أو Shadow DOM يعترض الحدث قبل وصوله إلى الخلية.
الإصلاح:
# Check if captcha is in an iframe
iframes = driver.find_elements(By.TAG_NAME, "iframe")
for iframe in iframes:
if "captcha" in iframe.get_attribute("src").lower():
driver.switch_to.frame(iframe)
break
# Now find and click cells
cells = driver.find_elements(By.CSS_SELECTOR, ".grid-cell")
شبكة ديناميكية — تتبدّل البلاطات بعد النقر
العرَض: كل نقرة صحيحة تكشف صورة جديدة، فلا يكتمل التحديد أبداً.
السبب: الشبكات الديناميكية بنمط reCAPTCHA تستبدل البلاطة المحلولة بصورة جديدة بعد كل نقرة.
الإصلاح: استخدم طريقة الرمز (token) بدلاً من طريقة الصورة مع reCAPTCHA:
# Token method handles dynamic grids automatically
response = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": "SITE_KEY",
"pageurl": "https://example.com",
"json": 1
})
أخطاء انتهاء المهلة
انتهاء صلاحية التحدي قبل عودة الحل
العرَض: الحل يعود متأخراً بعد أن يكون التحدي قد انتهت صلاحيته.
السبب: تنتهي صلاحية تحديات الشبكة عادةً خلال 2–3 دقائق من ظهورها.
الإصلاح:
- أرسل الصورة فور التقاطها مباشرة
- إذا تجاوز الحل 60 ثانية، حدِّث التحدي ثم أعد المحاولة
حلقة لا نهائية على CAPCHA_NOT_READY
العرَض: الاستطلاع يستمر بلا نهاية وترجع الخدمة
CAPCHA_NOT_READYباستمرار.
السبب: ربما فشلت المهمة بصمت دون أن تُرجِع خطأً صريحاً.
الإصلاح: حدِّد سقفاً لعدد المحاولات وعالج حالات الفشل:
for attempt in range(30):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") not in ["CAPCHA_NOT_READY"]:
break # Actual error, stop polling
raise Exception("Grid captcha solve failed — refresh and retry")
قائمة تحقق سريعة للتشخيص
راجع هذه العناصر بالترتيب قبل فتح تذكرة دعم؛ فمعظم حالات الفشل تنكشف عند أحد بنودها:
| العنصر | ما يجب التحقق منه |
|---|---|
| تنسيق الصورة | PNG أو JPEG بترميز base64 سليم |
| حجم الصورة | أقل من 600 كيلوبايت |
| اكتمال الشبكة | التقاط الشبكة كاملة مع هوامشها |
| جودة الصورة | واضحة، غير مضبَّبة ولا مُصغَّرة |
| صيغة الحل | تحليل الإحداثيات المفصولة بفواصل بدقة |
| أساس الفهرسة | تحويل الفهرسة من 1 إلى 0 للمصفوفات |
| سياق الـ iframe | التبديل إلى iframe الكابتشا عند وجوده |
| صلاحية التحدي | الإرسال فور الالتقاط مباشرة |
الأسئلة الشائعة
لماذا أحصل على ERROR_CAPTCHA_UNSOLVABLE مع صورة تبدو واضحة للعين؟
غالباً يختلف ما يراه المحلل عمّا تراه أنت: تراكب شفاف فوق الشبكة، أو ضغط أفقد الحدود حدّتها، أو التقاط بدقة أقل من الأصل. أعد الالتقاط بالدقة الكاملة ومن دون أي طبقة فوق الشبكة قبل أن تحكم على التحدي بأنه غامض فعلاً.
كيف أعرف أن الشبكة ديناميكية بنمط reCAPTCHA قبل أن أبدأ الحل؟
راقب سلوك البلاطات بعد النقر: إذا استُبدلت الخلية المحلولة بصورة جديدة بدل أن تبقى محدَّدة، فالشبكة ديناميكية. في هذه الحالة اترك طريقة الصورة إلى طريقة الرمز (userrecaptcha) التي تتولّى الشبكات المتغيّرة تلقائياً.
ما المدة المتاحة قبل انتهاء صلاحية تحدي الشبكة؟
تنتهي صلاحية معظم تحديات الصور الشبكية خلال 2–3 دقائق من ظهورها. لتفادي أخطاء المهلة أرسل الصورة فور التقاطها، ولا تُراكم لقطات لإرسالها دفعة واحدة لاحقاً.
أين أعالج خطأ الإزاحة بمقدار واحد: في الكود أم قبل الإرسال؟
عالجه في الكود عند تطبيق الحل، لا قبل الإرسال. الـ API يعيد إحداثيات مبنية على الفهرسة من 1، فاطرح 1 من كل قيمة قبل استخدامها فهرساً في مصفوفة الخلايا المبنية على 0.
هل يمكن تشغيل عدة عمليات حل للشبكة في آنٍ واحد؟
نعم. كل عملية حل مستقلة عن غيرها، ويعتمد عدد ما يمكن تشغيله بالتوازي على عدد الـ Threads المتاحة في خطتك. أرسل المهام معاً واستطلع نتيجة كل واحدة على حدة.