الشروحات المعمقة

كيف يعمل سير عمل التحدي والاستجابة في GeeTest v3

لحلّ GeeTest v3 عبر CaptchaAI تحتاج فعلياً إلى قيمتين فقط من الصفحة: gt وchallenge، وفي المقابل تستعيد ثلاث قيم — geetest_challenge وgeetest_validate وgeetest_seccode — هي ما يطلبه الموقع لإتمام التحقق. المشكلة أن GeeTest لا يكتفي برمز واحد كما في reCAPTCHA، بل يوزّع العملية على مرحلتين، ومن يتجاهل هذا الترتيب يحصل على قيم صحيحة شكلياً لكنها مرفوضة. وهذا الدليل يفكّك التدفق خطوة بخطوة.

بروتوكول من مرحلتين يحكم تبادل القيم

يفصل GeeTest v3 بين إنشاء التحدي والتحقق منه، ويوزّعهما على سياقين متتاليين:

  1. التسجيل على الخادم — تُنشأ فيه قيمتا gt وchallenge قبل أن يرى المستخدم شيئاً.
  2. العرض والتحقق في المتصفح — يُحلّ فيه التحدي ثم تُرسل القيم الثلاث الناتجة إلى الخادم لاعتمادها.

المرحلة الأولى: التسجيل على جانب الخادم

تتصل الواجهة الخلفية للموقع بخوادم GeeTest لتسجيل تحدٍّ جديد:

Site Backend → GeeTest Server: "Give me a challenge for this user"
GeeTest Server → Site Backend: { gt, challenge, new_captcha }
Site Backend → Browser: Passes gt and challenge to the page

المرحلة الأولى تجري بالكامل على الخادم قبل أن يرى المستخدم أي عنصر تفاعلي.

المرحلة الثانية: العرض والتحقق على جانب العميل والخادم

يعرض المتصفح التحدي، يحلّه المستخدم، ثم تعود القيم الناتجة إلى الخادم للتحقق منها لدى GeeTest:

Browser: Renders slider/puzzle using gt + challenge
User: Solves the challenge
Browser → Site Backend: { geetest_challenge, geetest_validate, geetest_seccode }
Site Backend → GeeTest Server: Verifies the three values
GeeTest Server → Site Backend: { result: "success" }

باختصار: gt وchallenge مدخلان للمرحلة الأولى، والقيم الثلاث مخرجات المرحلة الثانية، وأي تكامل آلي عليه أن يحاكي هذا الفصل.

تفكيك التدفق خطوة بخطوة

الخطوة 1: نداء واجهة التسجيل

تستدعي الواجهة الخلفية للموقع نقطة نهاية التسجيل الخاصة بـ GeeTest:

GET https://api.geetest.com/register.php?gt=GT_ID&json_format=1

الرد:

{
  "success": 1,
  "gt": "81dc9bdb52d04dc20036dbd8313ed055",
  "challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "new_captcha": true
}

تحمل الاستجابة ثلاث معلمات تهمّك:

  • gt — معرّف GeeTest الذي يحدّد حساب الموقع، وهو ثابت لا يتغيّر بين الجلسات.
  • challenge — رمز التحدي الفريد لهذه الجلسة تحديداً.
  • new_captcha — إشارة إلى ما إذا كان سيُستخدم تنسيق CAPTCHA الجديد.

انتبه: قيمة challenge مخصّصة للاستخدام مرة واحدة ومحدودة بالوقت. كل تحميل جديد للصفحة يولّد تحدياً جديداً، لذا لا تخزّنها لإعادة الاستعمال.

الخطوة 2: عرض التحدي في المتصفح

يستقبل المتصفح gt وchallenge ويهيّئ عنصر واجهة GeeTest عبر استدعاء initGeetest:

تُمرَّر القيمتان مباشرة إلى الدالة، ويُعاد الناتج عبر getValidate بعد نجاح الحل.

initGeetest({
  gt: "81dc9bdb52d04dc20036dbd8313ed055",
  challenge: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  offline: false,
  new_captcha: true,
  product: "float"
}, function(captchaObj) {
  captchaObj.appendTo('#captcha-container');
  captchaObj.onSuccess(function() {
    var result = captchaObj.getValidate();
    // result contains: geetest_challenge, geetest_validate, geetest_seccode
  });
});

الخطوة 3: أنواع تحديات GeeTest v3

لا يعرض GeeTest v3 قالباً واحداً، بل يختار الشكل حسب إعدادات الموقع وملف مخاطر المستخدم:

  • المنزلق (Slider): سحب قطعة اللغز لتحريكها وإكمال الصورة.
  • النقر على الأيقونات: النقر على أيقونات محددة وفق التسلسل المعروض.
  • النقر على الكلمات: النقر على الأحرف الصينية بالترتيب الصحيح.
  • التحدي المكاني: نقر أو اختيار يعتمد على التفكير المكاني.

والمهم عملياً أن مخرجات جميع هذه الأنواع واحدة: القيم الثلاث نفسها.

الخطوة 4: القيم الثلاث الناتجة عن الحل

بعد الحل، ينتج العنصر ثلاث قيم:

{
  "geetest_challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy",
  "geetest_validate": "abc123def456_validate",
  "geetest_seccode": "abc123def456_validate|jordan"
}

وهذه دلالة كل قيمة منها:

  • geetest_challenge — رمز التحدي المعدّل (الأصلي مضافاً إليه حرفان).
  • geetest_validate — تجزئة التحقق الناتجة عن الحل.
  • geetest_seccode — رمز الحماية، وهو قيمة validate مع اللاحقة \|jordan.

الخطوة 5: التحقق على جانب الخادم

ترسل الواجهة الخلفية هذه القيم الثلاث إلى GeeTest للتحقق النهائي:

POST https://api.geetest.com/validate.php

seccode=abc123def456_validate|jordan
&challenge=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy
&sdk=geetest-python-3.0.0

فيستجيب GeeTest بـ:

{
  "seccode": "abc123def456_validate",
  "validate": "abc123def456_validate"
}

استخراج gt وchallenge لتمريرهما إلى CaptchaAI

الجزء العملي الوحيد الذي يخصّك عند الأتمتة هو الحصول على gt وchallenge قبل انتهاء صلاحيتهما، وأمامك ثلاث طرق مرتّبة من الأمتن إلى الأسرع:

  1. اعتراض استجابة التسجيل مباشرة من الشبكة.
  2. القراءة من سمات عناصر DOM في الصفحة.
  3. الاستخراج من نص استدعاء initGeetest بتعبير نمطي.

الطريقة الأولى: اعتراض استجابة التسجيل

الأكثر موثوقية، لأنها تقرأ القيم من الشبكة مباشرة كما وصلت:

اعتراض الاستجابة يعمل حتى لو غيّر الموقع بنية صفحته، لأنه لا يعتمد على شكل الـ DOM.

from playwright.sync_api import sync_playwright
import json

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    geetest_params = {}

    def handle_response(response):
        if "register" in response.url and "geetest" in response.url:
            data = response.json()
            geetest_params["gt"] = data.get("gt")
            geetest_params["challenge"] = data.get("challenge")

    page.on("response", handle_response)
    page.goto("https://example.com/login")

    # Wait for GeeTest to load
    page.wait_for_selector(".geetest_holder")
    print(f"gt: {geetest_params.get('gt')}")
    print(f"challenge: {geetest_params.get('challenge')}")

الطريقة الثانية: القراءة من DOM الصفحة

مناسبة حين تكون القيم مضمّنة في سمات العناصر:

gt = page.evaluate("() => document.querySelector('[data-gt]')?.dataset.gt")
challenge = page.evaluate("() => document.querySelector('[data-challenge]')?.dataset.challenge")

الطريقة الثالثة: من استدعاء initGeetest

ابحث في مصدر الصفحة عن استدعاء initGeetest واستخرج القيم بتعبير نمطي:

import re
source = page.content()
gt_match = re.search(r"gt['\"]?\s*[:=]\s*['\"]([a-f0-9]{32})['\"]", source)
challenge_match = re.search(r"challenge['\"]?\s*[:=]\s*['\"]([a-f0-9]{32})['\"]", source)

إرسال الطلب إلى CaptchaAI واستلام القيم الثلاث

بعد استخراج gt وchallenge، أرسلهما إلى نقطة النهاية in.php مع method=geetest:

POST https://ocr.captchaai.com/in.php

key=YOUR_API_KEY
&method=geetest
&gt=81dc9bdb52d04dc20036dbd8313ed055
&challenge=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
&pageurl=https://example.com/login
&json=1

يتضمّن هذا الطلب الحقول الأساسية:

  • key — مفتاح الـ API الخاص بك.
  • method=geetest — نوع الحل المطلوب.
  • gt وchallenge — القيمتان المستخرجتان من الصفحة.
  • pageurl — عنوان الصفحة التي يظهر عليها التحدي.

ثم استطلع النتيجة دورياً عبر res.php:

GET https://ocr.captchaai.com/res.php?key=YOUR_API_KEY&action=get&id=TASK_ID&json=1

فتُرجع CaptchaAI:

{
  "status": 1,
  "request": {
    "geetest_challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy",
    "geetest_validate": "abc123def456_validate",
    "geetest_seccode": "abc123def456_validate|jordan"
  }
}

بهذا تتلقى القيم الثلاث المطلوبة لخطوة التحقق لدى الموقع، فتحقنها في حقول النموذج المناسبة وتُكمل الإرسال.

تذكّر أن هذه القيم صالحة لمرة واحدة؛ احقنها فور استلامها قبل أن تنتهي صلاحية challenge.

مثال تطبيقي: اختبار انحدار على بوابة حجز إقليمية

تخيّل فريق ضمان جودة في منصة حجز أو متجر إلكتروني بالمنطقة يشغّل اختبارات انحدار على صفحة تسجيل الدخول المحمية بـ GeeTest v3. الاختبار يحتاج إلى تسجيل دخول عشرات الجلسات المتوازية ليلاً قبل كل إصدار، ويمرّ كل مسار بالخطوات ذاتها:

  • استخراج زوج gt وchallenge جديد لكل جلسة على حدة، دون إعادة استخدام أي قيمة سابقة.
  • إرسال الزوج فوراً إلى CaptchaAI وحلّه قبل انتهاء صلاحيته القصيرة.
  • حقن القيم الثلاث العائدة في حقول النموذج ثم متابعة تدفق تسجيل الدخول.

هنا يظهر أثر نموذج CaptchaAI القائم على الـ Threads: التسعير يُحسب بعدد المهام المتزامنة لا بعدد عمليات الحل. خطة BASIC بسعر $15 شهرياً تتيح 5 threads تكفي دورة اختبار صغيرة، بينما تمنح خطة ADVANCE بسعر $90 شهرياً 50 thread لتشغيل دفعات أكبر بالتوازي دون رسوم لكل عملية حل.

الوضع المتصل مقابل غير المتصل

يملك GeeTest v3 وضعاً احتياطياً يعمل عندما يتعذّر الوصول إلى خوادمه:

  • متصل (Online): القيمة success تساوي 1، وهو تحدٍّ واستجابة اعتيادية عبر خوادم GeeTest.
  • غير متصل (Offline): القيمة success تساوي 0، وهو تحقق محلي مبسّط دون الرجوع إلى الخوادم.

في الوضع غير المتصل يُولَّد التحدي محلياً ويكون التحقق أبسط، غير أن الغالبية العظمى من المواقع تعتمد الوضع المتصل.

معالجة الأخطاء الشائعة

  • النتيجة لا تطابق الحالة الفعلية: غالباً لأن نوع CAPTCHA أو المعلمات لم تُطابَق مع الهدف الصحيح — قارن الهدف وطريقة الحل والمعلمات المطلوبة مجدداً.
  • الاختبار ينجح لكن بيئة الإنتاج تفشل: الجلسة أو الرؤوس أو سياق الوكيل يختلف عن الاختبار — أعد استخدام الشروط التي نجحت في الاختبار داخل سير العمل الفعلي كلما أمكن.
  • المشكلة ما تزال غامضة: السجلات لا تتضمن سياقاً كافياً لتشخيص موثوق — سجّل نوع الحل وزمن الاستجابة ورمز الخطأ والأثر اللاحق معاً.

الأسئلة الشائعة

هل يختلف الطلب باختلاف شكل التحدي (منزلق أو أيقونات)؟

لا. أنت ترسل gt وchallenge نفسيهما بصرف النظر عن شكل التحدي، وتستعيد القيم الثلاث ذاتها. ما لا يتغيّر مهما كان الشكل:

  • طريقة الإرسال إلى CaptchaAI (method=geetest).
  • بنية الاستجابة العائدة بالقيم الثلاث.

أما اختيار الشكل فيتم لدى GeeTest بناءً على إعدادات الموقع وملف المخاطر.

لماذا تُرفض القيم أحياناً رغم أن الحل يبدو صحيحاً؟

غالباً لأن challenge انتهت صلاحيته قبل إتمام الإرسال، أو لأنك استخرجت gt من عنصر GeeTest خاطئ حين تتعدد العناصر في الصفحة. استخرج زوجاً جديداً وأرسله فوراً، وتأكد من أنك تقرأ من العنصر الصحيح.

أين أحقن القيم الثلاث بعد استلامها من CaptchaAI؟

تُحقن في حقول النموذج المخفية التي يقرأها الخادم عند الإرسال، وهي عادةً geetest_challenge وgeetest_validate وgeetest_seccode. راجع اسم كل حقل في نموذج الموقع المستهدف قبل الحقن، فقد تختلف الأسماء أحياناً حسب التكامل.

كيف أوسّع الحل ليشمل جلسات متزامنة كثيرة؟

بما أن CaptchaAI يسعّر حسب عدد الـ threads لا حسب عمليات الحل، فإن التوسّع يعني رفع عدد الـ threads في خطتك. اجعل عدد الجلسات المتوازية مساوياً للـ threads المتاحة، وستحصل على عمليات حل غير محدودة داخل كل thread طوال الشهر.


الخطوات التالية

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.