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

دليل الكشف عن تنفيذ Cloudflare Turnstile

قبل أن تتمكن من حل تحدي Cloudflare Turnstile، تحتاج إلى اكتشافه على الصفحة واستخراج مفتاح الموقع. يمكن تضمين Turnstile عبر سمات HTML أو استدعاءات JavaScript API أو تحميلها ديناميكيًا بعد عرض الصفحة. يغطي هذا الدليل كل طرق الكشف — بدءًا من تحليل HTML البسيط وحتى تحليل JavaScript في وقت التشغيل.


طرق تنفيذ Turnstile

تقوم المواقع بتضمين Turnstile بثلاث طرق، تتطلب كل منها طريقة كشف مختلفة:

الطريقة كيف يعمل صعوبة الكشف
HTML ضمني <div class="cf-turnstile" data-sitekey="..."> في مصدر الصفحة سهل (HTML ثابت)
JavaScript صريحة تم استدعاء turnstile.render() في البرنامج النصي متوسط (تحليل JS)
** التحميل الديناميكي ** تم تحميل القطعة بعد إجراء المستخدم أو XHR صعب (يتطلب تنفيذ JS)

الطريقة الأولى: الكشف عن HTML ثابت

يستخدم أبسط تكامل Turnstile فئة cf-turnstile وسمة data-sitekey:

import re
import requests

def detect_turnstile_html(url):
    """Detect Turnstile from static HTML."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 Chrome/120.0.0.0",
        "Accept": "text/html,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.9",
    }

    response = requests.get(url, headers=headers, timeout=15)
    html = response.text

    result = {
        "turnstile_found": False,
        "sitekey": None,
        "mode": None,
        "theme": None,
        "action": None,
        "script_loaded": False,
    }

    # Check for Turnstile script
    if "challenges.cloudflare.com/turnstile" in html:
        result["script_loaded"] = True

    # Check for widget container
    if "cf-turnstile" in html:
        result["turnstile_found"] = True

        # Extract sitekey
        sitekey_match = re.search(
            r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html
        )
        if sitekey_match:
            result["sitekey"] = sitekey_match.group(1)

        # Extract mode
        if 'data-size="invisible"' in html:
            result["mode"] = "invisible"
        elif 'data-appearance="interaction-only"' in html:
            result["mode"] = "non-interactive"
        else:
            result["mode"] = "managed"

        # Extract theme
        theme_match = re.search(r'data-theme=["\'](\w+)["\']', html)
        if theme_match:
            result["theme"] = theme_match.group(1)

        # Extract action
        action_match = re.search(r'data-action=["\']([^"\']+)["\']', html)
        if action_match:
            result["action"] = action_match.group(1)

    return result


# Usage
info = detect_turnstile_html("https://example.com/login")
if info["turnstile_found"]:
    print(f"Sitekey: {info['sitekey']}")
    print(f"Mode: {info['mode']}")

الطريقة الثانية: اكتشاف JavaScript API

تستخدم بعض المواقع turnstile.render() بدلاً من سمات HTML:

import re

def detect_turnstile_js_api(html):
    """Detect Turnstile from JavaScript render calls."""
    patterns = [
        # turnstile.render('#element', {sitekey: '...'})
        r"turnstile\.render\s*\(\s*['\"]([^'\"]+)['\"]\s*,\s*\{([^}]+)\}",
        # turnstile.render(element, {sitekey: '...'})
        r"turnstile\.render\s*\([^,]+,\s*\{([^}]+)\}",
    ]

    for pattern in patterns:
        match = re.search(pattern, html, re.DOTALL)
        if match:
            config_text = match.group(match.lastindex)

            # Extract sitekey from config object
            sitekey_match = re.search(
                r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", config_text
            )
            # Extract callback
            callback_match = re.search(
                r"callback\s*:\s*(\w+|function)", config_text
            )
            # Extract action
            action_match = re.search(
                r"action\s*:\s*['\"]([^'\"]+)['\"]", config_text
            )
            # Extract appearance
            appearance_match = re.search(
                r"appearance\s*:\s*['\"]([^'\"]+)['\"]", config_text
            )

            return {
                "found": True,
                "method": "javascript_api",
                "sitekey": sitekey_match.group(1) if sitekey_match else None,
                "callback": callback_match.group(1) if callback_match else None,
                "action": action_match.group(1) if action_match else None,
                "appearance": appearance_match.group(1) if appearance_match else None,
            }

    return {"found": False, "method": None}

الطريقة الثالثة: اكتشاف التحميل الديناميكي (Selenium/Puppeteer)

عندما يتم تحميل Turnstile ديناميكيًا بعد تفاعل الصفحة:

بايثون (السيلينيوم)

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re

def detect_turnstile_dynamic(url):
    """Detect dynamically loaded Turnstile using Selenium."""
    options = webdriver.ChromeOptions()
    options.add_argument("--disable-blink-features=AutomationControlled")
    driver = webdriver.Chrome(options=options)

    try:
        driver.get(url)

        # Wait for page to fully load
        WebDriverWait(driver, 10).until(
            lambda d: d.execute_script("return document.readyState") == "complete"
        )

        result = {
            "turnstile_found": False,
            "sitekey": None,
            "iframe_present": False,
            "response_field": False,
        }

        # Check for Turnstile iframe
        iframes = driver.find_elements(By.CSS_SELECTOR, "iframe[src*='challenges.cloudflare.com']")
        if iframes:
            result["turnstile_found"] = True
            result["iframe_present"] = True

        # Check for cf-turnstile container
        containers = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile, [data-sitekey]")
        for container in containers:
            sitekey = container.get_attribute("data-sitekey")
            if sitekey:
                result["turnstile_found"] = True
                result["sitekey"] = sitekey

        # Check for hidden response field
        response_fields = driver.find_elements(
            By.CSS_SELECTOR, "[name='cf-turnstile-response'], [name='g-recaptcha-response']"
        )
        if response_fields:
            result["response_field"] = True

        # Check page source for JS API render
        page_source = driver.page_source
        js_match = re.search(
            r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", page_source
        )
        if js_match and not result["sitekey"]:
            result["sitekey"] = js_match.group(1)
            result["turnstile_found"] = True

        return result

    finally:
        driver.quit()

Node.js (Puppeteer)

const puppeteer = require("puppeteer");

async function detectTurnstileDynamic(url) {
  const browser = await puppeteer.launch({
    headless: "new",
    args: ["--disable-blink-features=AutomationControlled"],
  });

  const page = await browser.newPage();

  const result = {
    turnstileFound: false,
    sitekey: null,
    iframePresent: false,
    responseField: false,
    scriptUrl: null,
  };

  // Monitor network for Turnstile script
  page.on("response", (response) => {
    if (response.url().includes("challenges.cloudflare.com/turnstile")) {
      result.scriptUrl = response.url();
    }
  });

  await page.goto(url, { waitUntil: "networkidle2" });

  // Check for Turnstile container
  const sitekey = await page.evaluate(() => {
    const el = document.querySelector(
      ".cf-turnstile, [data-sitekey]"
    );
    return el ? el.getAttribute("data-sitekey") : null;
  });

  if (sitekey) {
    result.turnstileFound = true;
    result.sitekey = sitekey;
  }

  // Check for Turnstile iframe
  const iframes = await page.$$("iframe[src*='challenges.cloudflare.com']");
  if (iframes.length > 0) {
    result.turnstileFound = true;
    result.iframePresent = true;
  }

  // Check for response field
  const responseField = await page.$(
    "[name='cf-turnstile-response']"
  );
  result.responseField = !!responseField;

  await browser.close();
  return result;
}

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

فئة الكشف الشامل

import re
import requests

class TurnstileDetector:
    """Detect Cloudflare Turnstile across all implementation methods."""

    TURNSTILE_SCRIPT = "challenges.cloudflare.com/turnstile"
    SITEKEY_PATTERNS = [
        r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']',
        r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
        r"siteKey\s*[=:]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
        r"TURNSTILE_SITE_KEY\s*[=:]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
    ]

    def __init__(self, url, html=None):
        self.url = url
        self.html = html
        if not self.html:
            self._fetch()

    def _fetch(self):
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                          "AppleWebKit/537.36 Chrome/120.0.0.0",
            "Accept": "text/html,*/*;q=0.8",
            "Accept-Language": "en-US,en;q=0.9",
        }
        response = requests.get(self.url, headers=headers, timeout=15)
        self.html = response.text

    def detect(self):
        """Run all detection methods and return results."""
        return {
            "url": self.url,
            "turnstile_present": self.has_turnstile(),
            "sitekey": self.extract_sitekey(),
            "mode": self.detect_mode(),
            "implementation": self.detect_implementation(),
            "script_loaded": self.has_script(),
            "response_field": self.has_response_field(),
            "action": self.extract_action(),
            "theme": self.extract_theme(),
        }

    def has_turnstile(self):
        return (
            self.has_script()
            or "cf-turnstile" in self.html
            or self.extract_sitekey() is not None
        )

    def has_script(self):
        return self.TURNSTILE_SCRIPT in self.html

    def has_response_field(self):
        return "cf-turnstile-response" in self.html

    def extract_sitekey(self):
        for pattern in self.SITEKEY_PATTERNS:
            match = re.search(pattern, self.html)
            if match:
                return match.group(1)
        return None

    def detect_mode(self):
        if 'data-size="invisible"' in self.html or "size: 'invisible'" in self.html:
            return "invisible"
        if 'data-appearance="interaction-only"' in self.html:
            return "non-interactive"
        if "cf-turnstile" in self.html:
            return "managed"
        return "unknown"

    def detect_implementation(self):
        if "cf-turnstile" in self.html and re.search(r"data-sitekey=", self.html):
            return "html_implicit"
        if "turnstile.render" in self.html:
            return "javascript_explicit"
        if self.has_script() and not "cf-turnstile" in self.html:
            return "dynamic_loading"
        return "unknown"

    def extract_action(self):
        match = re.search(r'data-action=["\']([^"\']+)["\']', self.html)
        if match:
            return match.group(1)
        match = re.search(r"action\s*:\s*['\"]([^'\"]+)['\"]", self.html)
        return match.group(1) if match else None

    def extract_theme(self):
        match = re.search(r'data-theme=["\'](\w+)["\']', self.html)
        return match.group(1) if match else "auto"


# Usage
detector = TurnstileDetector("https://example.com/login")
info = detector.detect()

if info["turnstile_present"]:
    print(f"Sitekey: {info['sitekey']}")
    print(f"Mode: {info['mode']}")
    print(f"Implementation: {info['implementation']}")

الحل بعد الكشف

بمجرد اكتشافه، قم بحل المشكلة باستخدام CaptchaAI:

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_detected_turnstile(detection_result):
    """Solve Turnstile using detection results."""
    if not detection_result["turnstile_present"]:
        raise ValueError("No Turnstile detected")

    if not detection_result["sitekey"]:
        raise ValueError("Sitekey not found — may need browser-based extraction")

    params = {
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": detection_result["sitekey"],
        "pageurl": detection_result["url"],
        "json": 1,
    }

    # Include action if present
    if detection_result.get("action"):
        params["action"] = detection_result["action"]

    submit = requests.post("https://ocr.captchaai.com/in.php", data=params)
    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:
            return result["request"]

    raise TimeoutError("Turnstile solve timed out")


# Full workflow
detector = TurnstileDetector("https://example.com/signup")
info = detector.detect()

if info["turnstile_present"]:
    token = solve_detected_turnstile(info)
    print(f"Token: {token[:50]}...")

حالات الحافة

السيناريو التحدي الحل
مفتاح الموقع في ملف JS خارجي ليس في صفحة HTML تحليل ملفات JavaScript المرتبطة لأنماط مفتاح الموقع
مفتاح الموقع من استجابة API تم التحميل بعد استدعاء XHR مراقبة طلبات الشبكة لمفتاح الموقع في استجابات JSON
الحاجيات Turnstile متعددة مفاتيح مواقع مختلفة في نفس الصفحة قم بمطابقة مفتاح الموقع بالنموذج المحدد الذي ترسله
Turnstile في الظل DOM لا يمكن الوصول إليه عبر المحددات العادية استخدم shadowRoot.querySelector في سياق المتصفح
مفتاح الموقع المقدم من جانب الخادم مضمن في متغيرات القالب تحقق من علامات <script> لكائنات التكوين
Turnstile وراء المصادقة غير مرئية على الصفحة العامة المصادقة أولاً، ثم الكشف

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

أعراض السبب إصلاح
تم العثور على علامة البرنامج النصي ولكن لا يوجد مفتاح الموقع يتم عرض JS API بتكوين من مصدر آخر تحقق من جميع ملفات JS المرتبطة واستجابات XHR
تم استخراج مفتاح الموقع بشكل خاطئ أدوات CAPTCHA متعددة على الصفحة مطابقة مفاتيح الموقع مع عناصر النموذج المحيطة
يعمل الكشف ولكن فشل الحل معلمة الإجراء مطلوبة للتحقق من الصحة قم بتضمين قيمة data-action في طلب الحل
القطعة ليست في HTML الأولي التحميل الديناميكي بعد تفاعل المستخدم استخدم Selenium/Puppeteer لعرض الصفحة الكاملة
حقل cf-turnstile-response فارغ القطعة لم تكتمل بعد انتظر حتى تنتهي القطعة من التحميل

الأسئلة المتداولة

هل يمكن تغيير مفاتيح موقع Turnstile؟

نعم. يمكن لمشغلي الموقع تدوير مفاتيح الموقع في أي وقت. قم دائمًا باستخراج مفتاح الموقع حديثًا من الصفحة بدلاً من ترميزه.

هل أحتاج إلى معلمة الإجراء؟

فقط إذا كان الموقع يتحقق من صحة ذلك من جانب الخادم. إذا كان data-action موجودًا في HTML، فقم بإدراجه في طلب الحل الخاص بك للحصول على أفضل النتائج.

ماذا لو لم أتمكن من العثور على مفتاح الموقع؟

قد يكون مفتاح الموقع موجودًا في ملف JavaScript خارجي، أو استجابة API، أو يتم إنشاؤه ديناميكيًا. استخدم متصفح DevTools (علامة تبويب الشبكة) للعثور عليه، أو استخدم Selenium/Puppeteer لاستخراجه بعد عرض الصفحة بالكامل.

هل تؤثر طريقة الكشف على الحل؟

لا، يعمل حل Turnstile الخاص بـ CaptchaAI بنفس الطريقة بغض النظر عن كيفية تنفيذ الأداة. كل ما تحتاجه هو مفتاح الموقع وعنوان URL للصفحة.


ملخص

يتطلب اكتشاف Cloudflare Turnstile التحقق من علامة البرنامج النصي Turnstile وحاوية cf-turnstile وسمات data-sitekey واستدعاءات turnstile.render(). استخدم تحليل HTML الثابت لعمليات التكامل البسيطة وSelenium/Puppeteer لعناصر واجهة المستخدم المحملة ديناميكيًا. بمجرد اكتشافها، قم بحلها باستخدام حل Turnstile من CaptchaAIباستخدام مفتاح الموقع المستخرج - يتم التعامل مع جميع الأوضاع بشكل متماثل بمعدل نجاح عالٍ.

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

أدلة ذات صلة

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