استكشاف الأخطاء

التعامل مع reCAPTCHA v2 وCloudflare Turnstile على نفس الموقع

تستخدم بعض المواقع reCAPTCHA في صفحة تسجيل الدخول الخاصة بها وTurnstile في صفحة الخروج الخاصة بها. اختبار A/B الآخر بين مقدمي الخدمة - يعرض عنوان URL نفسه reCAPTCHA لزائر واحد وTurnstile لآخر. إن الترميز الثابت لطريقة حل واحدة يعني أن الأتمتة الخاصة بك تنقطع عندما تواجه النوع الآخر.

لماذا تستخدم المواقع موفري CAPTCHA متعددين

السيناريو كيف يظهر
صفحات مختلفة ومقدمو خدمات مختلفون تسجيل الدخول = reCAPTCHA، الخروج = Turnstile
موفرو اختبار A/B تعرض الصفحة نفسها عشوائيًا أيًا من النوعين
الهجرة قيد التقدم تحتوي الصفحات القديمة على reCAPTCHA، بينما تحتوي الصفحات الجديدة على Turnstile
التراجع عن الفشل فشل الموفر الأساسي في عودة → إلى الموفر الثانوي
الاختلاف الإقليمي reCAPTCHA لزوار الولايات المتحدة، Turnstile للاتحاد الأوروبي (GDPR)

بايثون: الكشف التلقائي والحل

import requests
import time
import re
from dataclasses import dataclass

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


@dataclass
class CaptchaInfo:
    provider: str     # "recaptcha" or "turnstile"
    method: str       # API method name
    sitekey: str
    pageurl: str
    response_field: str  # Form field name for the token


def detect_captcha_type(html, pageurl):
    """
    Detect which CAPTCHA provider is on the page.
    Returns CaptchaInfo or None.
    """
    # Check for Turnstile
    turnstile_match = re.search(
        r'class=["\'][^"\']*cf-turnstile[^"\']*["\'][^>]*data-sitekey=["\']([^"\']+)["\']',
        html,
    )
    if not turnstile_match:
        turnstile_match = re.search(
            r'data-sitekey=["\']([^"\']+)["\'][^>]*class=["\'][^"\']*cf-turnstile',
            html,
        )

    if turnstile_match:
        return CaptchaInfo(
            provider="turnstile",
            method="turnstile",
            sitekey=turnstile_match.group(1),
            pageurl=pageurl,
            response_field="cf-turnstile-response",
        )

    # Check for reCAPTCHA
    recaptcha_match = re.search(
        r'class=["\'][^"\']*g-recaptcha[^"\']*["\'][^>]*data-sitekey=["\']([^"\']+)["\']',
        html,
    )
    if not recaptcha_match:
        recaptcha_match = re.search(
            r'data-sitekey=["\']([^"\']+)["\'][^>]*class=["\'][^"\']*g-recaptcha',
            html,
        )

    # Also check for script-rendered reCAPTCHA
    if not recaptcha_match:
        recaptcha_match = re.search(
            r'grecaptcha\.render\([^,]+,\s*\{[^}]*["\']sitekey["\']\s*:\s*["\']([^"\']+)["\']',
            html,
        )

    if recaptcha_match:
        return CaptchaInfo(
            provider="recaptcha",
            method="userrecaptcha",
            sitekey=recaptcha_match.group(1),
            pageurl=pageurl,
            response_field="g-recaptcha-response",
        )

    return None


def solve_captcha(info):
    """Solve any detected CAPTCHA type via CaptchaAI."""
    params = {
        "key": API_KEY,
        "method": info.method,
        "json": 1,
    }

    if info.method == "userrecaptcha":
        params["googlekey"] = info.sitekey
        params["pageurl"] = info.pageurl
    elif info.method == "turnstile":
        params["sitekey"] = info.sitekey
        params["pageurl"] = info.pageurl

    resp = requests.post(SUBMIT_URL, data=params, timeout=30).json()
    if resp.get("status") != 1:
        raise RuntimeError(f"Submit failed: {resp.get('request')}")

    task_id = resp["request"]
    for _ in range(60):
        time.sleep(5)
        poll = requests.get(RESULT_URL, params={
            "key": API_KEY, "action": "get",
            "id": task_id, "json": 1,
        }, timeout=15).json()

        if poll.get("request") == "CAPCHA_NOT_READY":
            continue
        if poll.get("status") == 1:
            return poll["request"]
        raise RuntimeError(f"Solve failed: {poll.get('request')}")

    raise RuntimeError("Timeout")


def process_page(session, url):
    """Fetch page, detect CAPTCHA type, solve, and return form-ready data."""
    response = session.get(url)
    captcha_info = detect_captcha_type(response.text, url)

    if not captcha_info:
        print(f"No CAPTCHA detected on {url}")
        return None

    print(f"Detected {captcha_info.provider} on {url}")
    print(f"  Sitekey: {captcha_info.sitekey[:30]}...")

    token = solve_captcha(captcha_info)
    print(f"  Solved: {token[:30]}...")

    return {
        "provider": captcha_info.provider,
        "response_field": captcha_info.response_field,
        "token": token,
    }


# Usage: Handle multiple pages with different providers
session = requests.Session()

pages = [
    "https://example.com/login",      # Might have reCAPTCHA
    "https://example.com/checkout",   # Might have Turnstile
]

for url in pages:
    result = process_page(session, url)
    if result:
        form_data = {result["response_field"]: result["token"]}
        # Add other form fields...
        # session.post(url, data=form_data)

JavaScript: كشف CAPTCHA الديناميكي

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

function detectCaptchaType(html, pageurl) {
  // Turnstile
  const turnstileMatch = html.match(/cf-turnstile[^>]*data-sitekey=["']([^"']+)["']/);
  if (turnstileMatch) {
    return { provider: "turnstile", method: "turnstile", sitekey: turnstileMatch[1], pageurl, field: "cf-turnstile-response" };
  }

  // reCAPTCHA
  const recaptchaMatch = html.match(/g-recaptcha[^>]*data-sitekey=["']([^"']+)["']/);
  if (recaptchaMatch) {
    return { provider: "recaptcha", method: "userrecaptcha", sitekey: recaptchaMatch[1], pageurl, field: "g-recaptcha-response" };
  }

  // Script-rendered reCAPTCHA
  const scriptMatch = html.match(/sitekey["']\s*:\s*["']([^"']+)["']/);
  if (scriptMatch) {
    return { provider: "recaptcha", method: "userrecaptcha", sitekey: scriptMatch[1], pageurl, field: "g-recaptcha-response" };
  }

  return null;
}

async function solveCaptcha(info) {
  const body = new URLSearchParams({ key: API_KEY, method: info.method, json: "1" });
  if (info.method === "userrecaptcha") { body.set("googlekey", info.sitekey); body.set("pageurl", info.pageurl); }
  else if (info.method === "turnstile") { body.set("sitekey", info.sitekey); body.set("pageurl", info.pageurl); }

  const resp = await (await fetch(SUBMIT_URL, { method: "POST", body })).json();
  if (resp.status !== 1) throw new Error(`Submit: ${resp.request}`);

  const taskId = resp.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const url = `${RESULT_URL}?key=${API_KEY}&action=get&id=${taskId}&json=1`;
    const poll = await (await fetch(url)).json();
    if (poll.request === "CAPCHA_NOT_READY") continue;
    if (poll.status === 1) return poll.request;
    throw new Error(`Solve: ${poll.request}`);
  }
  throw new Error("Timeout");
}

async function processPage(url) {
  const response = await fetch(url);
  const html = await response.text();
  const info = detectCaptchaType(html, url);

  if (!info) { console.log(`No CAPTCHA on ${url}`); return null; }
  console.log(`${info.provider} detected on ${url}`);

  const token = await solveCaptcha(info);
  return { provider: info.provider, field: info.field, token };
}

// Usage
const pages = ["https://example.com/login", "https://example.com/checkout"];
for (const url of pages) {
  const result = await processPage(url);
  if (result) {
    console.log(`Solved ${result.provider}: ${result.token.substring(0, 30)}...`);
  }
}

مرجع الكشف عن الموفر

مزود علامة HTML عنوان URL للبرنامج النصي مجال الاستجابة
reCAPTCHA v2 class="g-recaptcha" google.com/recaptcha/api.js g-recaptcha-response
Cloudflare Turnstile class="cf-turnstile" challenges.cloudflare.com/turnstile cf-turnstile-response
hCaptcha class="h-captcha" js.hcaptcha.com/1/api.js h-captcha-response

استكشاف الأخطاء وإصلاحها

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

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

أدلة ذات صلة

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