دروس API

حل reCAPTCHA v2 مع Callback باستخدام CaptchaAI API

لحل reCAPTCHA v2 الذي يعتمد على رد النداء لا تحتاج إلى استدعاء مختلف لـ CaptchaAI API؛ أرسل الطلب نفسه كأي reCAPTCHA v2 عادي، لكن بدل كتابة الرمز الناتج في الحقل المخفي g-recaptcha-response، تستدعي دالة JavaScript التي سجّلها الموقع وتمرّر إليها الرمز. هذه الخطوة الأخيرة وحدها هي ما يميّز هذا النوع.

المشكلة التي تقود معظم المطوّرين إلى هنا واضحة: تحصل على رمز صحيح، تحقنه في الحقل المخفي، ثم لا يتفاعل النموذج إطلاقاً. السبب أن الموقع لا يقرأ ذلك الحقل أصلاً، بل ينتظر تشغيل دالة رد النداء. في الأسطر التالية نغطّي كيف تتعرّف على مواقع رد النداء، وكيف تحلّها عبر CaptchaAI، وكيف تستدعي الدالة بشكل صحيح، مع أمثلة جاهزة في Python وNode.js وPHP.

جديد على reCAPTCHA v2؟ ابدأ من دليل حل reCAPTCHA v2 القياسي عبر API لفهم التدفّق الأساسي، ثم عُد إلى هنا لتتعلّم متغيّر رد النداء.


الفرق بين المسار القياسي ومسار رد النداء

النداء إلى CaptchaAI متطابق في الحالتين؛ الفرق الوحيد هو ما تفعله بالرمز بعد استلامه. الجدول التالي يوضّح أين تتفرّع الخطوات:

الخطوة reCAPTCHA v2 القياسي reCAPTCHA v2 برد النداء
1. الإرسال إلى CaptchaAI method=userrecaptcha + مفتاح الموقع + رابط الصفحة مطابق
2. استطلاع النتيجة action=get + معرّف الكابتشا مطابق
3. استلام الرمز تنسيق الرمز نفسه مطابق
4. حقن الرمز تعيين قيمة الحقل g-recaptcha-response استدعاء دالة رد النداء وتمرير الرمز إليها
5. إرسال النموذج تشغيل إرسال النموذج تلقائي غالباً — تتكفّل به دالة رد النداء

مهم: لا تُعيّن قيمة g-recaptcha-response في المواقع التي تعتمد على رد النداء. تتجاهل الصفحة هذا الحقل وتنتظر تشغيل دالة رد النداء، وتعيينه دون استدعاء الدالة سيجعل الأمر يبدو وكأن الكابتشا لم تُحلّ إطلاقاً.


ما الذي تحتاجه قبل أن تبدأ

المتطلّب التفاصيل
مفتاح CaptchaAI API احصل عليه من captchaai.com/api.php. سلسلة من 32 حرفاً.
رابط الصفحة المستهدفة الرابط الكامل للصفحة التي تُحمَّل فيها أداة reCAPTCHA v2.
مفتاح موقع reCAPTCHA v2 المفتاح العام المرتبط بأداة reCAPTCHA على الصفحة.
أداة أتمتة متصفح Selenium أو Puppeteer أو Playwright — تحتاج إلى تنفيذ JavaScript لاستدعاء الدالة.
اسم دالة رد النداء دالة JavaScript التي يتوقّع الموقع أن تستقبل الرمز.

كيف تعرف أن الموقع يستخدم رد النداء

يكتب reCAPTCHA v2 القياسي الرمز المحلول داخل منطقة نص مخفية اسمها g-recaptcha-response، بينما تتخطّى تطبيقات رد النداء هذا الحقل وتستدعي دالة JavaScript مباشرة. للتمييز بينهما أمامك ثلاث طرق مرتّبة من الأسرع إلى الأعمق:

  • فحص السمة data-callback على عنصر الأداة في مصدر الصفحة.
  • قراءة خاصية callback داخل أي استدعاء grecaptcha.render().
  • تفتيش إعدادات reCAPTCHA الداخلية في الكائن ___grecaptcha_cfg.

الطريقة الأولى: فحص السمة data-callback

افحص عنصر أداة reCAPTCHA في مصدر الصفحة؛ فإن حمل السمة data-callback فالموقع يعتمد رد النداء، وقيمتها — SubmitToken في المثال التالي — هي اسم الدالة التي تحتاجها:

<div class="g-recaptcha"
     data-sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
     data-callback="SubmitToken">
</div>

الطريقة الثانية: فحص استدعاءات grecaptcha.render()

ابحث في شيفرة JavaScript الخاصة بالصفحة عن grecaptcha.render؛ الخاصية callback هي التي تسمّي الدالة المطلوبة — وهي userVerified في المثال التالي:

grecaptcha.render('recaptcha-container', {
  sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
  callback: userVerified
});

الطريقة الثالثة: فحص إعدادات reCAPTCHA الداخلية

افتح وحدة تحكم المتصفح (Console) على الصفحة المستهدفة، ونفّذ الأمر التالي ثم تنقّل داخل شجرة الكائن للوصول إلى الخاصية callback. يختلف المسار الدقيق من موقع لآخر — قد يكون clients[0].aa.l.callback أو غيره تبعاً لإصدار reCAPTCHA ودرجة تصغيره، وإن احتوت الصفحة على أكثر من أداة reCAPTCHA فافحص clients[1] وclients[2] وهكذا:

___grecaptcha_cfg.clients[0]

سكربت كشف سريع

نفّذ هذا في وحدة تحكم المتصفح للعثور على أسماء دوال رد النداء تلقائياً:

// Check data-callback attributes
document.querySelectorAll('[data-callback]').forEach(el => {
  console.log('data-callback:', el.getAttribute('data-callback'));
});

// Check internal config
if (typeof ___grecaptcha_cfg !== 'undefined') {
  Object.keys(___grecaptcha_cfg.clients).forEach(key => {
    const client = ___grecaptcha_cfg.clients[key];
    console.log(`Client ${key}:`, JSON.stringify(client, null, 2));
  });
}

مخطط مسار الحل

يلخّص المخطط التالي الدورة كاملة، من استخراج مفتاح الموقع حتى معالجة الموقع للرمز تلقائياً:

Page → extract sitekey + pageurl + callback name
                    ↓
      POST to in.php (method=userrecaptcha)
                    ↓
           receive captcha ID
                    ↓
         wait 15–20 seconds
                    ↓
      GET res.php (action=get, id=…)
          ↓                    ↓
   CAPCHA_NOT_READY       status=1 → token
    (wait 5s, retry)            ↓
                     invoke callback(token)
                              ↓
               site processes token automatically

تنفيذ الحل بلغة Python باستخدام Selenium

import time
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By

API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
PAGE_URL = "https://example.com/login"
CALLBACK_NAME = "SubmitToken"  # The callback function name from the page

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


def solve_recaptcha_v2(api_key, sitekey, pageurl):
    """Submit a reCAPTCHA v2 task and return the solved token."""

    # Step 1: Submit the captcha
    submit_resp = requests.post(
        SUBMIT_URL,
        data={
            "key": api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": 1,
        },
        timeout=30,
    )
    submit_resp.raise_for_status()
    submit_data = submit_resp.json()

    if submit_data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {submit_data}")

    captcha_id = submit_data["request"]
    print(f"Task created — captcha ID: {captcha_id}")

    # Step 2: Wait before first poll
    time.sleep(15)

    # Step 3: Poll for result
    for _ in range(60):
        result_resp = requests.get(
            RESULT_URL,
            params={
                "key": api_key,
                "action": "get",
                "id": captcha_id,
                "json": 1,
            },
            timeout=30,
        )
        result_resp.raise_for_status()
        result_data = result_resp.json()

        if result_data.get("request") == "CAPCHA_NOT_READY":
            time.sleep(5)
            continue

        if result_data.get("status") == 1:
            return result_data["request"]

        raise RuntimeError(f"Polling error: {result_data}")

    raise TimeoutError("reCAPTCHA v2 solve timed out")


def detect_callback_name(driver):
    """Detect the reCAPTCHA callback function name from the page."""

    # Try data-callback attribute first
    callback = driver.execute_script("""
        const el = document.querySelector('[data-callback]');
        if (el) return el.getAttribute('data-callback');
        return null;
    """)
    if callback:
        return callback

    # Try internal reCAPTCHA config
    callback = driver.execute_script("""
        if (typeof ___grecaptcha_cfg === 'undefined') return null;
        const clients = ___grecaptcha_cfg.clients;
        for (const key of Object.keys(clients)) {
            const client = clients[key];
            // Walk the object tree to find a callback function
            const json = JSON.stringify(client);
            const match = json.match(/"callback":"(\\w+)"/);
            if (match) return match[1];
        }
        return null;
    """)
    return callback


# Main workflow
driver = webdriver.Chrome()
driver.get(PAGE_URL)

# Detect the callback name (or use the known name)
detected = detect_callback_name(driver)
callback_name = detected or CALLBACK_NAME
print(f"Using callback: {callback_name}")

# Solve the CAPTCHA
token = solve_recaptcha_v2(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")

# Invoke the callback with the token
driver.execute_script(f"{callback_name}(arguments[0]);", token)
print("Callback invoked — site should process the token automatically")

# Wait for the page to process
time.sleep(3)
driver.quit()

ماذا يفعل هذا الكود:

  1. يرسل مفتاح الموقع ورابط الصفحة إلى in.php عبر method=userrecaptcha — بشكل مطابق تماماً للإصدار القياسي.
  2. يستطلع res.php كل 5 ثوانٍ حتى يجهز الرمز.
  3. يكتشف اسم دالة رد النداء من DOM الصفحة.
  4. يستدعي الدالة ويمرّر إليها الرمز المحلول عبر execute_script.
  5. تتكفّل شيفرة JavaScript الخاصة بالموقع بالباقي — إرسال النموذج أو التحقق أو إعادة توجيه الصفحة.

تنفيذ الحل بلغة Node.js باستخدام Puppeteer

النداء إلى CaptchaAI هو نفسه؛ ما يتغيّر فقط هو أسلوب حقن الرمز داخل المتصفح:

const puppeteer = require("puppeteer");

const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
const PAGE_URL = "https://example.com/login";
const CALLBACK_NAME = "SubmitToken";

const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveRecaptchaV2(apiKey, sitekey, pageurl) {
  // Step 1: Submit the captcha
  const submitResp = await fetch(SUBMIT_URL, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      key: apiKey,
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: pageurl,
      json: "1",
    }),
  });

  const submitData = await submitResp.json();
  if (submitData.status !== 1) {
    throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
  }

  const captchaId = submitData.request;
  console.log(`Task created — captcha ID: ${captchaId}`);

  // Step 2: Wait before first poll
  await sleep(15_000);

  // Step 3: Poll for result
  for (let i = 0; i < 60; i++) {
    const resultResp = await fetch(
      `${RESULT_URL}?${new URLSearchParams({
        key: apiKey,
        action: "get",
        id: captchaId,
        json: "1",
      })}`
    );

    const resultData = await resultResp.json();

    if (resultData.request === "CAPCHA_NOT_READY") {
      await sleep(5_000);
      continue;
    }

    if (resultData.status === 1) {
      return resultData.request;
    }

    throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
  }

  throw new Error("reCAPTCHA v2 solve timed out");
}

async function detectCallbackName(page) {
  return page.evaluate(() => {
    // Try data-callback attribute
    const el = document.querySelector("[data-callback]");
    if (el) return el.getAttribute("data-callback");

    // Try internal config
    if (typeof ___grecaptcha_cfg !== "undefined") {
      const clients = ___grecaptcha_cfg.clients;
      for (const key of Object.keys(clients)) {
        const json = JSON.stringify(clients[key]);
        const match = json.match(/"callback":"(\w+)"/);
        if (match) return match[1];
      }
    }

    return null;
  });
}

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto(PAGE_URL, { waitUntil: "networkidle2" });

  // Detect callback
  const detected = await detectCallbackName(page);
  const callbackName = detected || CALLBACK_NAME;
  console.log(`Using callback: ${callbackName}`);

  // Solve the CAPTCHA
  const token = await solveRecaptchaV2(API_KEY, SITEKEY, PAGE_URL);
  console.log(`Solved token: ${token.slice(0, 80)}...`);

  // Invoke the callback
  await page.evaluate(
    (name, tkn) => {
      window[name](tkn);
    },
    callbackName,
    token
  );
  console.log("Callback invoked — site should process the token automatically");

  await sleep(3_000);
  await browser.close();
})();

تنفيذ الحل بلغة PHP

النداء إلى API متطابق في PHP، لكن استدعاء دالة رد النداء يتطلّب سياق متصفح، لذا يغطّي هذا المثال الحل من جانب الخادم. استخدم أداة متصفح بلا واجهة — مثل PHP WebDriver — لخطوة الحقن.

<?php
$apiKey  = "YOUR_CAPTCHAAI_API_KEY";
$sitekey = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
$pageurl = "https://example.com/login";

// Step 1: Submit
$submit = file_get_contents("https://ocr.captchaai.com/in.php?" . http_build_query([
    "key"       => $apiKey,
    "method"    => "userrecaptcha",
    "googlekey" => $sitekey,
    "pageurl"   => $pageurl,
    "json"      => 1,
]));

$submitData = json_decode($submit, true);
if ($submitData["status"] !== 1) {
    die("Submit failed: " . $submit);
}

$captchaId = $submitData["request"];
echo "Task created — captcha ID: $captchaId\n";

// Step 2: Wait and poll
sleep(15);

for ($i = 0; $i < 60; $i++) {
    $result = file_get_contents("https://ocr.captchaai.com/res.php?" . http_build_query([
        "key"    => $apiKey,
        "action" => "get",
        "id"     => $captchaId,
        "json"   => 1,
    ]));

    $resultData = json_decode($result, true);

    if ($resultData["request"] === "CAPCHA_NOT_READY") {
        sleep(5);
        continue;
    }

    if ($resultData["status"] === 1) {
        $token = $resultData["request"];
        echo "Solved token: " . substr($token, 0, 80) . "...\n";
        // Pass $token to your browser automation to invoke the callback
        break;
    }

    die("Polling error: " . $result);
}

بعد الحصول على الرمز في PHP، استخدم أداة أتمتة متصفح — مثل php-webdriver — لتنفيذ ما يلي:

SubmitToken("TOKEN_FROM_CAPTCHAAI");

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

تخيّل فريق هندسة في شركة بالقاهرة أو الرياض يبني اختبارات جودة (QA) لبوابة حجز مواعيد داخلية تعرض reCAPTCHA v2. عند الضغط على "تأكيد"، لا تكتب البوابة الرمز في حقل مخفٍ، بل تستدعي دالة اسمها confirmBooking تُرسل النموذج مباشرة. وعندما يحاول الفريق أتمتة الاختبار عبر حقن g-recaptcha-response لا يحدث شيء، فتبدو الحالة وكأن الكابتشا لم تُحلّ.

الحل هنا هو المسار نفسه الموضّح أعلاه: أرسل الطلب إلى CaptchaAI، انتظر الرمز، ثم استدعِ confirmBooking ومرّر إليه الرمز. وللفرق التي تُشغّل مجموعات اختبار ليلية بأحجام كبيرة، تبدأ خطط CaptchaAI المعتمدة على الـ Threads من 15 دولاراً شهرياً (خطة BASIC، 5 threads) بحلول غير محدودة، ما يجعل تكلفة التشغيل ثابتة ويمكن توقّعها بغضّ النظر عن عدد مرّات إعادة تشغيل الاختبارات.


أشهر الأخطاء وكيفية تفاديها

# الخطأ ماذا يحدث الإصلاح
1 تعيين g-recaptcha-response بدل استدعاء الدالة تتجاهل الصفحة الرمز — ولا يُرسَل النموذج أبداً اعثر على اسم دالة رد النداء واستدعِها بالرمز
2 اسم دالة رد النداء خاطئ خطأ JavaScript: الدالة غير معرّفة راجع data-callback أو grecaptcha.render() أو الإعدادات الداخلية
3 الدالة موجودة على فهرس عميل مختلف استهداف أداة reCAPTCHA خاطئة في الصفحات متعددة الأدوات افحص ___grecaptcha_cfg.clients[1] وclients[2] وهكذا
4 استدعاء الدالة قبل جاهزية الصفحة لم تُعرَّف الدالة بعد في سياق الصفحة انتظر DOMContentLoaded أو networkidle قبل الاستدعاء
5 الاعتماد على اسم مبهم/مصغّر اسم الدالة في المصدر مشوّه بسبب التصغير استخدم وحدة تحكم المتصفح وقت التشغيل لإيجاد مرجع الدالة الفعلي
6 الخلط بين رد النداء والنسخة غير المرئية بعض التطبيقات غير المرئية تستخدم رد النداء أيضاً تحقّق من وجود data-size="invisible" — وإن وُجد فراجع حل reCAPTCHA غير المرئي عبر API

رسائل الخطأ الشائعة ومعناها

الجدول السابق يغطّي الأخطاء المفاهيمية في أثناء الإعداد؛ أمّا هنا فنركّز على رسائل الخطأ الحرفية التي قد تظهر لك وقت التشغيل، وما الذي تعنيه، وكيف تتصرّف حيالها مباشرة:

رسالة الخطأ أو العرض المعنى الإجراء
ReferenceError: SubmitToken is not defined لم تُحمَّل دالة رد النداء بعد، أو اسمها خاطئ أكّد الاسم من data-callback أو الإعدادات الداخلية، وانتظر اكتمال تحميل الصفحة، وتحقّق من window.SubmitToken في المواقع المصغّرة
ERROR_BAD_TOKEN_OR_PAGEURL زوج مفتاح الموقع/رابط الصفحة غير صالح — خطأ في API لا علاقة له بمسألة رد النداء مقابل القياسي أعد استخراج مفتاح الموقع ورابط الصفحة من الصفحة المستهدفة
ERROR_CAPTCHA_UNSOLVABLE تعذّر حل التحدي، وهذا ليس خاصاً برد النداء أعد المحاولة بطلب جديد
الرمز يعمل على reCAPTCHA v2 القياسي لكنه يفشل هنا — دون أي رسالة خطأ الصفحة على الأرجح تعتمد رد النداء اتبع خطوات الكشف للتأكيد، ثم انتقل من تعيين الحقل إلى استدعاء الدالة

للرجوع إلى مرجع الأخطاء الكامل، راجع أخطاء حل reCAPTCHA v2 الشائعة.


لماذا يناسب CaptchaAI هذه الحالة

خمسة عوامل تجعل CaptchaAI ملائماً تحديداً لمتغيّر رد النداء:

  • النداء نفسه لـ API: تدفّق الإرسال والاستطلاع مطابق لـ reCAPTCHA v2 القياسي، دون أي معطيات إضافية.
  • معدل حل مرتفع: يُطبَّق على reCAPTCHA v2 المدعوم، إذ يعتمد رد النداء والنسخة القياسية المحلّل نفسه.
  • سرعة الحل: أقل من 60 ثانية للرمز الواحد.
  • توافق الرمز: الرمز الناتج يعمل مع حقن g-recaptcha-response واستدعاء رد النداء معاً.
  • تسعير ثابت: خطط معتمدة على الـ Threads تبدأ من 15 دولاراً شهرياً بحلول غير محدودة.

الرمز الذي يُرجعه CaptchaAI واحد بغضّ النظر عن طريقة تنفيذ الموقع لـ reCAPTCHA v2؛ الفرق كله في شيفرتك من جانب العميل، أي في كيفية تسليم الرمز إلى الصفحة.


مثال كامل جاهز للتشغيل

هل تريد مشروعاً كاملاً يتضمّن إعداد البيئة والاستطلاع وإعادة المحاولة ومعالجة الأخطاء؟ راجع المثال الكامل الجاهز للتشغيل على GitHub →


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

هل تكلفة حل النسخة المعتمدة على رد النداء أعلى من reCAPTCHA v2 العادي؟

لا. يستخدم CaptchaAI الطلب نفسه (method=userrecaptcha) والمحلّل نفسه للنوعين، والتسعير معتمد على الـ Threads لا على نوع الكابتشا؛ لذلك لا توجد رسوم إضافية لمتغيّر رد النداء. تبدأ الخطط من 15 دولاراً شهرياً بحلول غير محدودة.

كم يستغرق حل reCAPTCHA v2 عبر CaptchaAI؟

عادةً أقل من 60 ثانية للرمز الواحد، مع معدل حل مرتفع على reCAPTCHA v2. ويتأثّر الوقت الفعلي بحِمل التشغيل وعدد الـ Threads المتاحة في خطتك.

هل أحتاج إلى بروكسي (proxy) لحل reCAPTCHA v2 برد النداء؟

لا تتطلّب الطريقة الموضّحة أعلاه تمرير بروكسي؛ يكفي مفتاح الموقع ورابط الصفحة. ورد النداء لا يغيّر ذلك، لأنه يخصّ خطوة تسليم الرمز داخل المتصفح فقط.

ماذا أفعل إذا كان اسم دالة رد النداء مبهماً أو مصغّراً (minified)؟

لا تعتمد على المصدر المصغّر؛ ابحث عن مرجع الدالة الفعلي وقت التشغيل على الصفحة الحيّة عبر ثلاث خطوات مرتّبة:

  1. تحقّق من السمة data-callback على عنصر الأداة أولاً.
  2. راجع ___grecaptcha_cfg.clients في وحدة تحكم المتصفح.
  3. تحقّق مما إذا كانت الدالة مُسندة إلى window باسم يمكنك استدعاؤه مباشرة.

خطوات البدء السريع

  1. احصل على مفتاح APIcaptchaai.com/api.php
  2. حدّد اسم دالة رد النداء — افحص data-callback أو grecaptcha.render() أو الإعدادات الداخلية
  3. انسخ كود Python أو Node.js أعلاه — واستبدل العناصر النائبة بمفتاحك ومفتاح الموقع ورابط الصفحة واسم الدالة
  4. شغّله — يصل الرمز في أقل من 60 ثانية، تُستدعى الدالة، ويعالج الموقع النتيجة
  5. واجهت مشكلة؟ ابدأ من أخطاء حل reCAPTCHA v2 الشائعة أو اقرأ مستندات CaptchaAI API كاملة

مقالات ذات صلة

أدلة ذات صلة

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