Bir Cloudflare Turnstile sorununu çözebilmeniz için önce onu sayfada tespit etmeniz ve site anahtarını çıkarmanız gerekir. Turnstile, HTML nitelikleri, JavaScript API çağrıları yoluyla gömülebilir veya sayfa oluşturulduktan sonra dinamik olarak yüklenebilir. Bu kılavuz, basit HTML ayrıştırmasından çalışma zamanı JavaScript analizine kadar her algılama yöntemini kapsar.
Turnstile uygulama yöntemleri
Siteler Turnstileyi her biri farklı bir algılama yaklaşımı gerektiren üç şekilde yerleştirir:
| Yöntem | Nasıl çalışır? | Tespit zorluğu |
|---|---|---|
| HTML örtülü | Sayfa kaynağında <div class="cf-turnstile" data-sitekey="..."> |
Kolay (statik HTML) |
| JavaScript açık | turnstile.render() komut dosyasında çağrıldı |
Orta (JS'yi ayrıştır) |
| Dinamik yükleme | Kullanıcı eylemi veya XHR sonrasında yüklenen widget | Zor (JS yürütülmesini gerektirir) |
Yöntem 1: Statik HTML algılama
En basit Turnstile entegrasyonu cf-turnstile sınıfını ve data-sitekey özelliğini kullanır:
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://staging.example.com/qa-login")
if info["turnstile_found"]:
print(f"Sitekey: {info['sitekey']}")
print(f"Mode: {info['mode']}")
Yöntem 2: JavaScript API algılaması
Bazı siteler HTML nitelikleri yerine turnstile.render() kullanır:
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}
Yöntem 3: Dinamik yükleme algılama (Selenium/Puppeteer)
Turnstile sayfa etkileşiminden sonra dinamik olarak yüklendiğinde:
Python (Selenyum)
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("--no-sandbox")
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: ["--no-sandbox"],
});
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://staging.example.com/qa-login").then(console.log);
Kapsamlı algılama sınıfı
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://staging.example.com/qa-login")
info = detector.detect()
if info["turnstile_present"]:
print(f"Sitekey: {info['sitekey']}")
print(f"Mode: {info['mode']}")
print(f"Implementation: {info['implementation']}")
Tespitten sonra çözme
Tespit edildikten sonra CaptchaAI ile çözün:
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]}...")
Kenar kasaları
| Senaryo | Mücadele | Çözüm |
|---|---|---|
| Harici JS dosyasındaki site anahtarı | Sayfa HTML'sinde değil | Site anahtarı kalıpları için bağlantılı JavaScript dosyalarını ayrıştırma |
| API yanıtından Site anahtarı | XHR çağrısından sonra yüklendi | JSON yanıtlarında site anahtarına yönelik ağ isteklerini izleyin |
| Çoklu Turnstile widget'ları | Aynı sayfada farklı site anahtarları | Site anahtarını gönderdiğiniz spesifik formla eşleştirin |
| Gölge DOM'da Turnstile | Normal seçicilerle erişilemez | shadowRoot.querySelector'yi tarayıcı bağlamında kullanın |
| Sunucu tarafında oluşturulan site anahtarı | Şablon değişkenlerine gömülü | Yapılandırma nesneleri için <script> etiketlerini kontrol edin |
| Kimlik doğrulamanın arkasındaki Turnstile | Herkese açık sayfada görünmüyor | Önce kimlik doğrulaması yapın, ardından tespit edin |
Sorun giderme
| Belirti | Sebep | Düzeltme |
|---|---|---|
| Komut dosyası etiketi bulundu ancak site anahtarı yok | Başka bir kaynaktan gelen yapılandırmayla JS API oluşturma | Bağlantılı tüm JS dosyalarını ve XHR yanıtlarını kontrol edin |
| Yanlış site anahtarı çıkarıldı | Sayfada birden fazla CAPTCHA widget'ı | Site anahtarlarını çevredeki form öğeleriyle eşleştirin |
| Algılama çalışıyor ancak çözüm başarısız oluyor | Doğrulama için gerekli eylem parametresi | data-action değerini çözüm isteğine dahil edin |
| Widget ilk HTML'de değil | Kullanıcı etkileşiminden sonra dinamik yükleme | Tam sayfa oluşturmak için Selenium/Puppeteer kullanın |
cf-turnstile-response alanı boş |
Widget henüz tamamlanmadı | Widget'ın yüklemeyi bitirmesini bekleyin |
Sık sorulan sorular
Turnstile site anahtarları değişebilir mi?
Evet. Site operatörleri site anahtarlarını istedikleri zaman döndürebilir. Site anahtarını sabit kodlamak yerine her zaman sayfadan taze olarak çıkarın.
Action parametresine ihtiyacım var mı?
Yalnızca site bunu sunucu tarafında doğrularsa. HTML'de data-action mevcutsa, en iyi sonuçları elde etmek için bunu çözüm isteğinize ekleyin.
Site anahtarını bulamazsam ne olur?
Site anahtarı harici bir JavaScript dosyasında, bir API yanıtında olabilir veya dinamik olarak oluşturulmuş olabilir. Bulmak için tarayıcı DevTools'u (Ağ sekmesi) kullanın veya tam sayfa oluşturulduktan sonra çıkarmak için Selenium/Puppeteer kullanın.
Tespit yöntemi çözümü etkiler mi?
Hayır. CaptchaAI'nin Turnstile çözücüsü, widget'ın nasıl uygulandığına bakılmaksızın aynı şekilde çalışır. Yalnızca site anahtarına ve sayfa URL'sine ihtiyacınız var.
Özet
Cloudflare Turnstile'yi tespit etmek Turnstile komut dosyası etiketinin, cf-turnstile konteynerinin, data-sitekey niteliklerinin ve turnstile.render() çağrılarının kontrol edilmesini gerektirir. Basit entegrasyonlar için statik HTML ayrıştırmayı ve dinamik olarak yüklenen widget'lar için Selenium/Puppeteer'yi kullanın. Tespit edildikten sonra şununla çözün:CaptchaAI'nin Turnstile çözücüsüçıkarılan site anahtarını kullanarak — tüm modlar %100 başarı oranıyla aynı şekilde işlenir.