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

استخراج روابط anchor وbframe في reCAPTCHA

في معظم الحالات لن تحتاج إلى استخراج رابط anchor أو bframe؛ إذ يكفي CaptchaAI مفتاح الموقع (sitekey) وعنوان الصفحة (pageurl) لحل reCAPTCHA v2. لكنّ فهم بنية الإطارين يصبح أداة تشخيص مهمة في ثلاث حالات:

  • رفض الرمز رغم صحّة الإعداد الظاهري.
  • وجود أكثر من نسخة reCAPTCHA في الصفحة نفسها.
  • تحميل مفتاح الموقع ديناميكياً بعد رسم الصفحة.

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


كيف يركّب reCAPTCHA إطارَي anchor وbframe

يُعرض reCAPTCHA داخل إطارَي iframe متداخلين يحملان معلمات مشفّرة تحتاجها بعض سير عمل الأتمتة المتقدمة:

  • إطار anchor: يحمل مربع الاختيار «I'm not a robot» والتحليل المبدئي للمخاطر، وهو ظاهر منذ تحميل الصفحة.
  • إطار bframe: يحمل شبكة تحدي الصور، ولا يُحمَّل إلا عند النقر على مربع الاختيار.
Target page (example.com/login)
    └── <iframe src="https://www.google.com/recaptcha/api2/anchor?...">
        │   ← Anchor iframe: "I'm not a robot" checkbox
        │
        └── <iframe src="https://www.google.com/recaptcha/api2/bframe?...">
                ← Bframe iframe: Image challenge grid (loads when clicked)

إطار anchor: مربع الاختيار والتحليل المبدئي

يظهر رابط anchor كالتالي، حاملاً مفتاح الموقع والأصل والإصدار:

https://www.google.com/recaptcha/api2/anchor?
    ar=1
    &k=6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp  ← site key
    &co=aHR0cHM6Ly9leGFtcGxlLmNvbTo0NDM.           ← encoded origin
    &hl=en                                           ← language
    &v=jF2Zb_rr_5sv8dMHoGIn-XxY                    ← reCAPTCHA version
    &size=normal                                     ← widget size
    &cb=89fu2pf0swif                                ← callback ID

إطار bframe: شبكة تحدي الصور

أما رابط إطار bframe فأبسط، ويقتصر على اللغة والإصدار ومفتاح الموقع:

https://www.google.com/recaptcha/api2/bframe?
    hl=en
    &v=jF2Zb_rr_5sv8dMHoGIn-XxY
    &k=6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp

معلمات رابط anchor وما الذي يعنيه كلٌّ منها

المعلمة الاسم الوصف
k مفتاح الموقع مفتاح موقع reCAPTCHA
co الأصل المشفّر الأصل المشفّر بترميز Base64 (البروتوكول + النطاق + المنفذ)
v الإصدار تجزئة إصدار حزمة reCAPTCHA JavaScript
hl اللغة رمز لغة التحدي
size الحجم normal أو compact أو invisible
cb رد النداء معرّف دالة رد النداء الفريد
theme السمة light أو dark
ar نسبة العرض علامة نسبة العرض إلى الارتفاع

فك تشفير المعلمة co لاستخراج الأصل

تحمل المعلمة co الأصل مشفّراً بـ base64، ويكفي فكّه لمعرفة نطاق تهيئة reCAPTCHA:

import base64

co_value = "aHR0cHM6Ly9leGFtcGxlLmNvbTo0NDM."
# Remove trailing period (padding artifact)
decoded = base64.b64decode(co_value.rstrip(".") + "==").decode()
print(decoded)  # "https://example.com:443"

يكشف هذا النطاق الأصلي الذي ضُبط عليه reCAPTCHA، وهي نقطة مهمة عند تشخيص رفض الرموز.


طرق استخراج رابطَي anchor وbframe

تختلف الطريقة بحسب ظهور reCAPTCHA في شيفرة الصفحة الأولية أم حقنه ديناميكياً بعد التحميل.

الاستخراج من مصدر الصفحة باستخدام Python

حين يظهر إطار anchor في مصدر HTML، تقرأ معلماته بـ BeautifulSoup:

import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, parse_qs
import re
import base64

def extract_recaptcha_iframes(url):
    """Extract reCAPTCHA anchor and bframe iframe URLs and 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 = requests.get(url, headers=headers, timeout=15)
    soup = BeautifulSoup(response.text, "html.parser")

    result = {
        "anchor_url": None,
        "bframe_url": None,
        "site_key": None,
        "origin": None,
        "version": None,
        "language": None,
    }

    # Find anchor iframe
    anchor_iframe = soup.find("iframe", src=re.compile(r"recaptcha.*anchor"))
    if anchor_iframe:
        anchor_url = anchor_iframe.get("src", "")
        result["anchor_url"] = anchor_url

        # Parse parameters
        parsed = urlparse(anchor_url)
        params = parse_qs(parsed.query)

        result["site_key"] = params.get("k", [None])[0]
        result["version"] = params.get("v", [None])[0]
        result["language"] = params.get("hl", [None])[0]

        # Decode origin
        co = params.get("co", [None])[0]
        if co:
            try:
                padded = co.rstrip(".") + "=="
                result["origin"] = base64.b64decode(padded).decode()
            except Exception:
                result["origin"] = co

    # Find bframe iframe (may not be in source — loaded dynamically)
    bframe_iframe = soup.find("iframe", src=re.compile(r"recaptcha.*bframe"))
    if bframe_iframe:
        result["bframe_url"] = bframe_iframe.get("src", "")

    # Construct bframe URL from anchor parameters if not found
    if not result["bframe_url"] and result["site_key"] and result["version"]:
        result["bframe_url"] = (
            f"https://www.google.com/recaptcha/api2/bframe?"
            f"hl={result['language'] or 'en'}"
            f"&v={result['version']}"
            f"&k={result['site_key']}"
        )

    return result

iframes = extract_recaptcha_iframes("https://example.com/login")
print(f"Site key: {iframes['site_key']}")
print(f"Origin: {iframes['origin']}")
print(f"Anchor URL: {iframes['anchor_url']}")

الاستخراج باستخدام Node.js

المنطق نفسه في Node.js، مع axios وcheerio لقراءة سمة src:

const axios = require("axios");
const cheerio = require("cheerio");
const { URL } = require("url");

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

    const $ = cheerio.load(html);
    const result = {
        anchorUrl: null,
        bframeUrl: null,
        siteKey: null,
        origin: null,
        version: null,
    };

    // Find anchor iframe
    const anchorIframe = $("iframe[src*='recaptcha'][src*='anchor']");
    if (anchorIframe.length) {
        const src = anchorIframe.attr("src");
        result.anchorUrl = src;

        const url = new URL(src);
        result.siteKey = url.searchParams.get("k");
        result.version = url.searchParams.get("v");

        // Decode origin
        const co = url.searchParams.get("co");
        if (co) {
            try {
                result.origin = Buffer.from(
                    co.replace(/\.$/, ""), "base64"
                ).toString();
            } catch {}
        }
    }

    // Construct bframe URL
    if (result.siteKey && result.version) {
        result.bframeUrl =
            `https://www.google.com/recaptcha/api2/bframe?` +
            `hl=en&v=${result.version}&k=${result.siteKey}`;
    }

    return result;
}

extractRecaptchaIframes("https://example.com/login").then(console.log);

الاستخراج من الصفحات الديناميكية باستخدام Selenium

حين يُحقن reCAPTCHA بعد رسم الصفحة، يلزمك متصفح فعلي مثل Selenium:

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

def extract_iframes_selenium(url):
    """Extract reCAPTCHA iframe URLs from a dynamically loaded page."""
    driver = webdriver.Chrome()
    driver.get(url)
    time.sleep(3)  # Wait for reCAPTCHA to load

    result = {"anchor_url": None, "bframe_url": None}

    # Find all iframes
    iframes = driver.find_elements(By.TAG_NAME, "iframe")

    for iframe in iframes:
        src = iframe.get_attribute("src") or ""
        if "recaptcha" in src and "anchor" in src:
            result["anchor_url"] = src
        elif "recaptcha" in src and "bframe" in src:
            result["bframe_url"] = src

    driver.quit()
    return result

متى تحتاج فعلاً إلى رابطَي anchor وbframe؟

لا تحتاج معظم سير العمل إلى هذه الروابط لأن CaptchaAI API تطلب sitekey وpageurl فقط، لكنّ أربع حالات تجعل استخراجها مفيداً:

  1. التأكد من مفتاح الموقع الصحيح.
  2. تحديد إصدار reCAPTCHA.
  3. مطابقة الأصل في التطبيقات صارمة النطاق.
  4. تشخيص حالات فشل الحل.

تخيّل أداة أتمتة لبوابة حجوزات في السعودية تعرض reCAPTCHA v2 بنسختين في الصفحة: واحدة للنموذج العربي وأخرى للإنجليزي. هنا يميّز رابط anchor مفتاح الموقع الصحيح لكل نسخة قبل الإرسال.

1. التأكد من مفتاح الموقع الصحيح

عندما تحتوي الصفحة على عدة نسخ من reCAPTCHA أو يُحمَّل المفتاح ديناميكياً:

# Extract sitekey from anchor URL when it's not in the page HTML
iframes = extract_recaptcha_iframes(url)
sitekey = iframes["site_key"]  # Reliably present in the iframe URL

2. تحديد إصدار reCAPTCHA

يكشف مسار الرابط النوع الدقيق قبل اختيار الحل:

# The anchor URL reveals the exact reCAPTCHA version
if "/api2/anchor" in anchor_url:
    recaptcha_type = "v2"
elif "/enterprise/anchor" in anchor_url:
    recaptcha_type = "enterprise"

3. مطابقة الأصل في التطبيقات صارمة النطاق

مرّر الأصل المفكوك بوصفه pageurl حين يفرض الموقع تطابق النطاق:

# Decode the origin from the co parameter
origin = decode_co_parameter(iframes["co"])
# Use this origin as the pageurl for the solver

4. تشخيص حالات فشل الحل

عند رفض الرموز، تكشف مقارنة معلمات anchor مع طلب الحل أي تعارض في مفتاح الموقع أو النطاق:

def debug_solve_params(anchor_url, solver_pageurl, solver_sitekey):
    """Compare anchor params with solver request to find mismatches."""
    parsed = urlparse(anchor_url)
    params = parse_qs(parsed.query)

    issues = []

    # Check sitekey
    anchor_key = params.get("k", [None])[0]
    if anchor_key != solver_sitekey:
        issues.append(f"Sitekey mismatch: anchor={anchor_key}, solver={solver_sitekey}")

    # Check origin
    co = params.get("co", [None])[0]
    if co:
        origin = base64.b64decode(co.rstrip(".") + "==").decode()
        solver_parsed = urlparse(solver_pageurl)
        solver_origin = f"{solver_parsed.scheme}://{solver_parsed.netloc}"
        if origin != solver_origin:
            issues.append(f"Origin mismatch: anchor={origin}, solver={solver_origin}")

    return issues if issues else ["No mismatches found"]

الطريقة القياسية والموصى بها للحل

تجاوز استخراج الإطارات واحصل على الرمز مباشرةً عبر CaptchaAI بمفتاح الموقع وعنوان الصفحة فقط:

import requests
import time

API_KEY = "YOUR_API_KEY"

# All you need: sitekey + pageurl
submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
    "pageurl": "https://example.com/login",
    "json": 1,
})

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

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[:50]}...")
        break
  • يكفي هذا المسار في معظم التكاملات.
  • لا يلزم استخراج anchor أو bframe إلا حين يفشل الحل بسبب مشكلات في مفتاح الموقع أو النطاق.

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

هل يشترط CaptchaAI تمرير رابطَي anchor وbframe لحل reCAPTCHA؟

لا. تحتاج الخدمة إلى sitekey وpageurl فقط، وتتولّى داخلياً التفاعل مع الإطارين. استخراج الروابط مفيد للتشخيص لا للحل.

ما الفرق العملي بين إطار anchor وإطار bframe؟

الفارق في التوقيت ومحتوى كل إطار:

  • anchor: ظاهر منذ تحميل الصفحة، ويضم مربع الاختيار "I'm not a robot" ومفتاح الموقع في رابطه.
  • bframe: يُنشَأ لاحقاً بواسطة JavaScript عند النقر وظهور تحدي الصور، ولهذا لا تجده في شيفرة HTML الأولية وتحتاج إلى Selenium أو Puppeteer لالتقاط رابطه.

كيف أحصل على مفتاح الموقع عندما لا يظهر في شيفرة HTML؟

اقرأه من رابط anchor مباشرةً؛ فالمعلمة k موجودة فيه دائماً حتى عند تحميل المفتاح ديناميكياً. استخدم دالة extract_recaptcha_iframes أعلاه ثم مرّر القيمة بوصفها sitekey.

هل تختلف روابط reCAPTCHA Enterprise عن reCAPTCHA v2؟

نعم في المسار. يحمل reCAPTCHA v2 المسار /api2/anchor، بينما يستخدم Enterprise المسار /enterprise/anchor؛ وعليه يعتمد مثال تحديد الإصدار. وكلا النوعين مدعوم للحل عبر CaptchaAI.


الخلاصة

  • يعتمد reCAPTCHA على إطارَي iframe: anchor لمربع الاختيار وbframe لتحدي الصور.
  • يحمل رابط anchor مفتاح الموقع والأصل المشفّر وتجزئة الإصدار.
  • للحل القياسي عبر CaptchaAI يكفيك sitekey وpageurl دون أي استخراج.
  • تبقى تقنيات الاستخراج أداتك عند تشخيص فشل التحقق من النطاق أو انتزاع المفاتيح من صفحات ديناميكية.

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

أدلة ذات صلة

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