Teknik Açıklamalar

Cloudflare Turnstile Uygulama Tespit Kılavuzu

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.

İlgili Makaleler

Bu makale için yorumlar devre dışı bırakılmıştır.