دروس API

شرح معلمة reCAPTCHA Data-S

هل ينجح حل reCAPTCHA على المواقع العادية ثم يفشل فجأة على صفحة «حركة مرور غير عادية» في بحث Google؟ السبب الأكثر شيوعاً هو معلمة data-s مفقودة: رمز جلسة تولّده خوادم Google وتُدرجه داخل عنصر reCAPTCHA، ويجب إرساله مع طلب الحل وإلا حصلت على رمز غير صالح. إليك الخلاصة أولاً:

  • ما هي؟ رمز جلسة يربط تحدي reCAPTCHA بجلسة الخادم.
  • أين تظهر؟ غالباً على خصائص Google مثل بحث Google وYouTube.
  • ماذا تفعل؟ استخرجها وأرسلها مع طلب الحل عند وجودها فقط.

متى تحتاج إلى معلمة data-s؟

لا تظهر data-s في غالبية تطبيقات reCAPTCHA، بل تكاد تقتصر على خصائص Google. يوضّح الجدول أين تتوقع ظهورها:

الموقع مدى الظهور ملاحظات
بحث Google (صفحة sorry/الحركة غير العادية) دائماً مطلوبة لحل CAPTCHA بحث Google
YouTube أحياناً تظهر في بعض مسارات التحقق
Google Play أحياناً التحقق عند إدراج التطبيقات
Google Forms نادراً تطبيقات محدودة
مواقع الأطراف الثالثة التي تستخدم reCAPTCHA تقريباً لا تظهر عمليات التكامل القياسية لا تستخدم data-s

قاعدة سريعة قبل أن تقرر:

  • موقع طرف ثالث عادي: تجاهل data-s واكتفِ بـ sitekey وpageurl.
  • CAPTCHA بحث Google: توقّع وجود data-s في كل مرة تقريباً.
  • رموز غير صالحة على خصائص Google وحدها: ابدأ التشخيص من data-s.

مثال عملي: فريق تطوير في القاهرة يبني أداة لمتابعة ترتيب نتائج البحث تُرسل عشرات الاستعلامات من عنوان IP واحد داخل مركز بيانات. سرعان ما تعرض Google صفحة «حركة مرور غير عادية» تحمل data-s، وتجاهُلها يعني فشل كل محاولة حل رغم أن sitekey صحيح تماماً.


ما هي معلمة data-s وكيف تعمل؟

data-s رمز جلسة يولّده الخادم ويُضمَّن داخل شيفرة HTML الخاصة بعنصر reCAPTCHA. وظيفته ربط تحدي CAPTCHA بجلسة محددة على الخادم، ما يمنع إعادة استخدام الرمز الناتج في جلسة أخرى.

أين تظهر داخل الصفحة

<!-- reCAPTCHA widget with data-s parameter -->
<div class="g-recaptcha"
     data-sitekey="6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp"
     data-s="AB2grfE8_kyMp3XYRuJo5c..."
     data-callback="onCaptchaSolved">
</div>

تظهر السمة data-s إلى جوار data-sitekey المعتاد داخل عنصر <div> الخاص بـ reCAPTCHA.

خصائص المعلمة الأساسية

الخاصية القيمة
التنسيق سلسلة بترميز Base64 يتراوح طولها بين 200 و500 حرف
العُمر تُستخدم مرة واحدة وترتبط بعملية تحميل الصفحة الحالية
النطاق خاصة بالجلسة ولا يمكن إعادة استخدامها بين عمليات التحميل
إلزامية؟ نعم عند وجودها — يفشل الحل بدونها
التحديث تُولَّد قيمة جديدة مع كل تحميل أو تحديث للصفحة

نقاط تستحق الانتباه:

  • مرّرها كما هي بترميز base64 دون إعادة تشفير.
  • عاملها كقيمة تُستهلك مرة واحدة ثم تُهمل.
  • تتغير data-s مع كل تحميل بعكس sitekey الثابت.

آلية العمل خطوة بخطوة

User triggers CAPTCHA (e.g., Google flags unusual search traffic)
    ↓
Google serves a CAPTCHA page with:

  - data-sitekey (site key, same for all Google search CAPTCHAs)
  - data-s (session token, unique per page load)
    ↓
reCAPTCHA widget initializes with both parameters
    ↓
Challenge completion generates a g-recaptcha-response token
    ↓
Token is submitted alongside the session reference
    ↓
Google validates token + session binding
    ↓
If data-s was not used during solving: "invalid-input-response" or silent failure

تعمل data-s عملياً كـ nonce، أي رقم يُستعمل لمرة واحدة يربط التحدي بجلسة محددة على الخادم. على مزوّد الحل أن يستعمل هذه القيمة أثناء توليد الرمز حتى يطابق الناتج الجلسة المتوقعة.


استخراج قيمة data-s من الصفحة

اقرأ سمة data-s مباشرة قبل كل طلب حل، لأن القيمة تتغير مع كل تحميل. الخطوات نفسها مهما اختلفت اللغة:

  1. حمّل صفحة الـ CAPTCHA أو اقرأ شيفرة HTML.
  2. ابحث عن عنصر <div> الذي يحمل data-sitekey.
  3. اقرأ سمة data-s إن وُجدت وأرسلها فوراً.

عبر Python

import requests
from bs4 import BeautifulSoup
import re

def extract_recaptcha_params(url):
    """Extract reCAPTCHA parameters including data-s from a page."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/120.0.0.0 Safari/537.36",
    }

    response = requests.get(url, headers=headers, timeout=15)
    soup = BeautifulSoup(response.text, "html.parser")

    # Find reCAPTCHA widget div
    widget = soup.find("div", class_="g-recaptcha")
    if not widget:
        # Try finding by data-sitekey attribute
        widget = soup.find(attrs={"data-sitekey": True})

    if not widget:
        return {"error": "No reCAPTCHA widget found"}

    params = {
        "sitekey": widget.get("data-sitekey"),
        "data_s": widget.get("data-s"),
        "callback": widget.get("data-callback"),
        "size": widget.get("data-size"),
        "has_data_s": widget.get("data-s") is not None,
    }

    return params

# Example: Google "unusual traffic" page
params = extract_recaptcha_params("https://www.google.com/sorry/index")
print(params)
# {
#   "sitekey": "6LfwuyUT...",
#   "data_s": "AB2grfE8_kyMp3...",
#   "has_data_s": True
# }

عبر Node.js

const axios = require("axios");
const cheerio = require("cheerio");

async function extractRecaptchaParams(url) {
    const { data: html } = await axios.get(url, {
        headers: {
            "User-Agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
                "AppleWebKit/537.36 (KHTML, like Gecko) " +
                "Chrome/120.0.0.0 Safari/537.36",
        },
        timeout: 15000,
    });

    const $ = cheerio.load(html);
    const widget = $(".g-recaptcha, [data-sitekey]").first();

    if (widget.length === 0) {
        return { error: "No reCAPTCHA widget found" };
    }

    return {
        sitekey: widget.attr("data-sitekey"),
        dataS: widget.attr("data-s") || null,
        callback: widget.attr("data-callback") || null,
        hasDataS: !!widget.attr("data-s"),
    };
}

extractRecaptchaParams("https://www.google.com/sorry/index")
    .then(console.log);

عبر Selenium للصفحات الديناميكية

عندما تُحمَّل الصفحة عبر JavaScript ولا تظهر data-s في شيفرة HTML الأولية، استخدم Selenium لقراءة القيمة بعد اكتمال التحميل:

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

def extract_data_s_selenium(driver, url):
    """Extract data-s from a dynamically loaded reCAPTCHA page."""
    driver.get(url)

    # Wait for reCAPTCHA widget to load
    import time
    time.sleep(3)

    try:
        widget = driver.find_element(By.CSS_SELECTOR, ".g-recaptcha, [data-sitekey]")
        return {
            "sitekey": widget.get_attribute("data-sitekey"),
            "data_s": widget.get_attribute("data-s"),
        }
    except Exception:
        return {"error": "Widget not found"}

إرسال معلمة data-s إلى CaptchaAI

عند وجود data-s، أضِفها إلى حمولة طلب CaptchaAI إلى جانب googlekey وpageurl:

Python

import requests
import time

API_KEY = "YOUR_API_KEY"

# Step 1: Extract parameters from the CAPTCHA page
sitekey = "6LfwuyUTAAAAAOAmoS0fdqijC2PbbdH4kjq62Y1b"
data_s = "AB2grfE8_kyMp3XYRuJo5c..."  # Extracted from data-s attribute
page_url = "https://www.google.com/sorry/index?continue=..."

# Step 2: Submit to CaptchaAI WITH data-s
submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url,
    "data-s": data_s,  # Include data-s parameter
    "json": 1,
})

task_id = submit.json()["request"]

# Step 3: Poll for result
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,
        "json": 1,
    }).json()

    if result.get("status") == 1:
        token = result["request"]
        print(f"Token: {token[:60]}...")
        # Submit this token to the Google CAPTCHA form
        break

Node.js

const axios = require("axios");

async function solveWithDataS(sitekey, dataS, pageUrl) {
    const API_KEY = "YOUR_API_KEY";

    // Submit with data-s
    const { data: submit } = await axios.post(
        "https://ocr.captchaai.com/in.php",
        new URLSearchParams({
            key: API_KEY,
            method: "userrecaptcha",
            googlekey: sitekey,
            pageurl: pageUrl,
            "data-s": dataS,
            json: 1,
        })
    );

    const taskId = submit.request;

    // Poll
    for (let i = 0; i < 60; i++) {
        await new Promise((r) => setTimeout(r, 5000));
        const { data: result } = await axios.get(
            "https://ocr.captchaai.com/res.php",
            {
                params: {
                    key: API_KEY,
                    action: "get",
                    id: taskId,
                    json: 1,
                },
            }
        );

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

    throw new Error("Timeout");
}

نقاط يجدر تذكّرها:

  • يُرسل الحقل باسمه الحرفي data-s دون تعديل.
  • reCAPTCHA v2 مدعوم عبر الطريقة userrecaptcha.
  • استطلع res.php حتى تصل status إلى 1 ثم استخدم الرمز.

أداة استخراج جاهزة لإعادة الاستخدام

الأداة التالية تتعامل مع الصفحات التي تحوي data-s وتلك التي لا تحويها، وتبني حمولة الطلب تلقائياً بالمعلمات الصحيحة:

import requests
from bs4 import BeautifulSoup

class RecaptchaExtractor:
    """Extract reCAPTCHA parameters from any page."""

    def __init__(self, url, session=None):
        self.url = url
        self.session = session or requests.Session()
        self.params = None

    def extract(self):
        """Extract sitekey, data-s, and other parameters."""
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                          "AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/120.0.0.0 Safari/537.36",
        }

        response = self.session.get(self.url, headers=headers, timeout=15)
        soup = BeautifulSoup(response.text, "html.parser")

        widget = soup.find(attrs={"data-sitekey": True})
        if not widget:
            raise ValueError("No reCAPTCHA widget found on page")

        self.params = {
            "sitekey": widget["data-sitekey"],
            "pageurl": self.url,
        }

        # Include data-s only if present
        data_s = widget.get("data-s")
        if data_s:
            self.params["data-s"] = data_s

        return self.params

    def build_solver_payload(self, api_key):
        """Build CaptchaAI submission payload with correct parameters."""
        if not self.params:
            self.extract()

        payload = {
            "key": api_key,
            "method": "userrecaptcha",
            "googlekey": self.params["sitekey"],
            "pageurl": self.params["pageurl"],
            "json": 1,
        }

        # Only include data-s when it exists
        if "data-s" in self.params:
            payload["data-s"] = self.params["data-s"]

        return payload


# Usage
extractor = RecaptchaExtractor("https://www.google.com/sorry/index?continue=...")
payload = extractor.build_solver_payload("YOUR_API_KEY")
# payload includes data-s only when present on the page

استخدمها في الحالات التالية:

  • صفحات مختلطة، بعضها يحوي data-s وبعضها لا.
  • بناء الحمولة تلقائياً دون شروط يدوية متكررة.
  • إعادة استخدام المنطق نفسه عبر عدة سكربتات أتمتة.

أخطاء شائعة عند التعامل مع data-s

يجمع الجدول أكثر الأخطاء تكراراً وإصلاح كل منها:

الخطأ العَرَض الإصلاح
حذف data-s رغم وجودها فشل صامت في التحقق من الرمز تحقق من وجود السمة قبل كل إرسال
إرسال data-s مع عدم وجودها خطأ في الحل أو رفض الطلب أرسلها فقط إذا كانت موجودة على العنصر
إعادة استخدام data-s بين تحميلات الصفحة رمز غير صالح استخرج قيمة جديدة لكل تحميل للصفحة
ترميز data-s بشكل خاطئ داخل الرابط معلمة مشوّهة مرّر قيمة base64 الأصلية دون ترميز إضافي
استخدام قيمة قديمة (مضى على تحميل الصفحة دقائق) عدم تطابق الرمز استخرج data-s مباشرة قبل الإرسال إلى الخدمة

عادات تقيك هذه الأخطاء:

  • تحقّق من السمة برمجياً بدل افتراض وجودها.
  • سجّل data-s عند فشل الحل لتتبّع السبب.
  • استخرج قيمة طازجة مع كل محاولة إعادة.

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

كيف أتأكد من وجود معلمة data-s على الصفحة قبل الإرسال؟

اقرأ سمة data-s من عنصر <div> أثناء الاستخراج. إذا وُجدت القيمة أضِفها إلى الطلب، وإلا اكتفِ بـ sitekey وpageurl. أدوات الاستخراج أعلاه تُعيد حقلاً منطقياً مثل has_data_s يخبرك إن كانت المعلمة على الصفحة أم لا.

هل تظهر data-s في reCAPTCHA v3 وreCAPTCHA Enterprise؟

المعلمة مرتبطة بجلسة الخادم وليست بإصدار reCAPTCHA بحد ذاته، لكنها في الممارسة العملية تكاد تنحصر في تطبيقات Google الداخلية على صفحات مثل بحث Google. أما تطبيقات reCAPTCHA v3 وEnterprise على مواقع الأطراف الثالثة فتعتمد عادةً على sitekey وaction دون data-s.

ما الفرق بين data-s و data-sitekey؟

data-sitekey مفتاح عام ثابت يعرّف الموقع ولا يتغير بين الزيارات. أما data-s فرمز جلسة مؤقت يتغير مع كل تحميل ويربط التحدي بجلسة الخادم. تحتاج sitekey دائماً، وdata-s فقط عند وجودها على العنصر.

هل يمكنني تخزين قيمة data-s وإعادة استخدامها لاحقاً؟

لا. القيمة خاصة بالجلسة ومرتبطة بتحميل صفحة واحد، وتُولَّد قيمة جديدة مع كل تحميل. استخرج قيمة طازجة قبل كل طلب حل، وإلا حصلت على رمز غير صالح.


الخلاصة

معلمة data-s رمز يربط التحدي بجلسة الخادم، ويظهر في تطبيقات reCAPTCHA محددة أبرزها الخصائص المملوكة لـ Google. عند وجوده استخرجه من شيفرة HTML الخاصة بعنصر reCAPTCHA وأضِفه إلى طلب CaptchaAI إلى جانب sitekey وpageurl. القاعدة العملية باختصار:

  • تحقّق من data-s قبل كل إرسال.
  • أضِفها عند وجودها واحذفها عند غيابها.
  • على صفحات بحث Google تحديداً، تكون data-s مطلوبة دائماً.

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

أدلة ذات صلة

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