قبل أن تتمكن من حل تحدي 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باستخدام مفتاح الموقع المستخرج - يتم التعامل مع جميع الأوضاع بشكل متماثل بمعدل نجاح عالٍ.
مقالات ذات صلة
- Cloudflare Challenge مقابل اكتشاف Turnstile
- مقارنة Geetest وCloudflare Turnstile
- Cloudflare Turnstile 403 بعد إصلاح الرمز المميز