Tutorials

CaptchaAI ile Django Uygulamalarında CAPTCHA Kullanımı

Django projelerinde CAPTCHA iki farklı yönde karşınıza çıkar ve bu ikisini ayırmak, doğru kodu yazmanın ilk adımıdır:

  • Doğrulama: Kendi formunuza gelen token'ı sunucu tarafında Cloudflare'e sorarsınız — bu bir güvenlik önlemidir ve CaptchaAI gerektirmez.
  • Çözme: Dışarıdaki bir CAPTCHA korumalı sayfa için geçerli bir token üretirsiniz — izinli veri toplama, QA testi veya otomasyon; CaptchaAI'nin devreye girdiği yer burasıdır.

İkisi tamamen ayrı kod yolları ister ve karıştırılmaları en sık yapılan hatadır. Bu rehber her iki tarafı da CaptchaAI ile, doğrudan kopyalayıp çalıştırabileceğiniz Django örnekleriyle gösterir.

Türkiye'deki ekiplerin çoğu için asıl cazip nokta öngörülebilir maliyet: CaptchaAI thread bazlı çalışır ve fiyatları USD üzerinden sabittir (BASIC $15/ay, 5 thread), yani kur oynaklığından etkilenmezsiniz. Aşağıdaki servis sınıfını bir kez yazarsınız; reCAPTCHA, Turnstile ve resim CAPTCHA'larının hepsi aynı submit/sorgulama akışını kullanır.


Senaryo 1: Django formlarınıza gelen CAPTCHA token'ını doğrulama

Formunuza Turnstile ya da reCAPTCHA eklediğinizde iş bitmiş olmaz. Widget yalnızca tarayıcıda bir token üretir; bu token'ı sunucu tarafında Cloudflare'e (ya da Google'a) sormadan gerçek bir doğrulama yapmış sayılmazsınız. Aksi halde bir bot formu doğrudan POST ederek widget'ı tümüyle atlayabilir.

Turnstile'ı bir Django formuna bağlama

Formunuza gizli bir alan ekleyin; widget üretilen token'ı bu alana yazar:

# forms.py
from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)
    cf_turnstile_response = forms.CharField(
        widget=forms.HiddenInput(),
        required=True,
    )

Görünümde token'ı gizli anahtarınızla birlikte Cloudflare'in siteverify uç noktasına gönderir ve yalnızca success dönerse formu işlersiniz:

# views.py
import requests
from django.conf import settings
from django.shortcuts import render, redirect
from .forms import ContactForm

def contact_view(request):
    if request.method == "POST":
        form = ContactForm(request.POST)
        if form.is_valid():
            # Verify Turnstile token with Cloudflare
            token = form.cleaned_data["cf_turnstile_response"]
            verification = requests.post(
                "https://challenges.cloudflare.com/turnstile/v0/siteverify",
                data={
                    "secret": settings.TURNSTILE_SECRET_KEY,
                    "response": token,
                    "remoteip": request.META.get("REMOTE_ADDR"),
                },
            ).json()

            if verification.get("success"):
                # Process the form
                return redirect("success")
            else:
                form.add_error(None, "CAPTCHA verification failed")
    else:
        form = ContactForm()

    return render(request, "contact.html", {
        "form": form,
        "turnstile_sitekey": settings.TURNSTILE_SITE_KEY,
    })

Şablon tarafında widget'ı yerleştirmek ve Cloudflare betiğini yüklemek yeterli:

<!-- templates/contact.html -->
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <div class="cf-turnstile" data-sitekey="{{ turnstile_sitekey }}"></div>
    <button type="submit">Send</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

Bu akışta CaptchaAI'ye ihtiyacınız yoktur — kendi formunuzu koruyorsunuz ve doğrulamayı doğrudan Cloudflare yapıyor. CaptchaAI ikinci senaryoda devreye girer.


Senaryo 2: Harici sitelerdeki CAPTCHA'ları CaptchaAI ile çözme

Django uygulamanız, sizin kontrolünüzde olmayan bir CAPTCHA korumalı sayfayla (izinli bir veri toplama işi, bir QA test akışı veya bir entegrasyon) etkileşime girmesi gerektiğinde durum tersine döner: artık token'ı doğrulamıyor, üretiyorsunuz. CaptchaAI'ye sitekey ve pageurl gönderir, dönen token'ı hedef isteğinize eklersiniz.

Yeniden kullanılabilir bir CaptchaAI hizmet sınıfı

Tüm çözüm mantığını tek bir serviste toplamak en temiz yaklaşımdır. Bu sınıf görevi in.php'ye gönderir, res.php'yi periyodik olarak sorgular ve token'ı döndürür:

# services/captcha_solver.py
import time
import requests
from django.conf import settings


class CaptchaSolverService:
    """Django service for solving CAPTCHAs via CaptchaAI."""

    API_BASE = "https://ocr.captchaai.com"

    def __init__(self):
        self.api_key = settings.CAPTCHAAI_API_KEY

    def solve_recaptcha_v2(self, sitekey, page_url, invisible=False):
        """Solve reCAPTCHA v2."""
        params = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": page_url,
            "json": 1,
        }
        if invisible:
            params["invisible"] = 1
        return self._submit_and_poll(params)

    def solve_turnstile(self, sitekey, page_url, action=None):
        """Solve Cloudflare Turnstile."""
        params = {
            "key": self.api_key,
            "method": "turnstile",
            "sitekey": sitekey,
            "pageurl": page_url,
            "json": 1,
        }
        if action:
            params["action"] = action
        return self._submit_and_poll(params)

    def solve_image(self, image_base64):
        """Solve image/text CAPTCHA."""
        return self._submit_and_poll({
            "key": self.api_key,
            "method": "base64",
            "body": image_base64,
            "json": 1,
        })

    def get_balance(self):
        """Check API balance."""
        response = requests.get(f"{self.API_BASE}/res.php", params={
            "key": self.api_key,
            "action": "getbalance",
            "json": 1,
        }, timeout=30)
        return float(response.json().get("request", 0))

    def _submit_and_poll(self, params, timeout=120):
        """Submit task and poll for result."""
        # Submit
        response = requests.post(f"{self.API_BASE}/in.php", data=params, timeout=30)
        response.raise_for_status()
        data = response.json()

        if data.get("status") != 1:
            raise CaptchaSolveError(f"Submit failed: {data.get('request')}")

        task_id = data["request"]

        # Poll
        start = time.time()
        while time.time() - start < timeout:
            time.sleep(5)
            result = requests.get(f"{self.API_BASE}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1,
            }, timeout=30).json()

            if result.get("status") == 1:
                return result["request"]
            if result.get("request") == "ERROR_CAPTCHA_UNSOLVABLE":
                raise CaptchaSolveError("CAPTCHA unsolvable")

        raise CaptchaSolveError("Solve timed out")


class CaptchaSolveError(Exception):
    pass

Üç ayrı metot (solve_recaptcha_v2, solve_turnstile, solve_image) aynı iç akışı paylaşır; yeni bir tür eklemek istediğinizde yalnızca method parametresini değiştiren bir metot daha yazmanız yeterli. get_balance ise bakiyenizi kontrol eder ve düşük bakiye uyarısı kurmak için idealdir.

Django ayarlarının yapılandırılması

API anahtarınızı ve Turnstile anahtarlarınızı ayarlara alın. Örnekteki değerler yer tutucudur; gerçek anahtarları ortam değişkenlerinden okuyun (bkz. SSS):

# settings.py
CAPTCHAAI_API_KEY = "YOUR_API_KEY"
TURNSTILE_SITE_KEY = "0x4AAAAAAAC3DHQhMMQ_Rxrg"
TURNSTILE_SECRET_KEY = "0x4AAAAAAAC3DHQhYYY_secret"

Hizmet sınıfını Django görünümlerinde kullanma

Servis hazır olduğunda onu senkron bir görünümden, bir yönetim komutundan veya bir Celery görevinden aynı şekilde çağırırsınız. İlk iki kullanım aşağıda.

Harici veri toplama görünümü

Bu görünüm bir hedef URL alır, Turnstile'ı çözer ve token'ı korunan kaynağa erişmek için kullanır:

# views.py
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from .services.captcha_solver import CaptchaSolverService, CaptchaSolveError

@require_POST
def scrape_external_data(request):
    """Solve CAPTCHA and fetch data from external CAPTCHA-protected site."""
    url = request.POST.get("target_url")
    if not url:
        return JsonResponse({"error": "target_url required"}, status=400)

    solver = CaptchaSolverService()

    try:
        # Solve the CAPTCHA
        token = solver.solve_turnstile(
            sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg",
            page_url=url,
        )

        # Use token to access the protected resource
        import requests as http_requests
        response = http_requests.post(url, data={
            "cf-turnstile-response": token,
        }, timeout=30)

        return JsonResponse({
            "status": "success",
            "data": response.text[:1000],
        })

    except CaptchaSolveError as e:
        return JsonResponse({"error": str(e)}, status=500)

Böyle bir işi yalnızca erişim yetkiniz olan sitelerde çalıştırın. Kişisel veri topluyorsanız Türkiye'de KVKK kapsamına girersiniz; CaptchaAI kullanımını yetkili QA ve veri toplama akışlarıyla sınırlı tutun.

Django yönetim komutu ile CAPTCHA çözme

Tek seferlik işler ve zamanlanmış görevler için komut satırından çözmek pratiktir. Aşağıdaki komut türü, sitekey ve URL alır, token'ı ve kalan bakiyeyi yazdırır:

# management/commands/solve_captcha.py
from django.core.management.base import BaseCommand
from myapp.services.captcha_solver import CaptchaSolverService


class Command(BaseCommand):
    help = "Solve a CAPTCHA and print the token"

    def add_arguments(self, parser):
        parser.add_argument("--type", choices=["recaptcha", "turnstile"], required=True)
        parser.add_argument("--sitekey", required=True)
        parser.add_argument("--url", required=True)

    def handle(self, *args, **options):
        solver = CaptchaSolverService()

        self.stdout.write(f"Solving {options['type']} for {options['url']}...")

        if options["type"] == "recaptcha":
            token = solver.solve_recaptcha_v2(options["sitekey"], options["url"])
        else:
            token = solver.solve_turnstile(options["sitekey"], options["url"])

        self.stdout.write(self.style.SUCCESS(f"Token: {token[:50]}..."))

        # Check balance
        balance = solver.get_balance()
        self.stdout.write(f"Remaining balance: ${balance:.2f}")

Çalıştırmak için:

python manage.py solve_captcha --type turnstile --sitekey 0x4AAA... --url https://example.com

Async Django görünümlerinde CAPTCHA çözme

Web'e bakan bir görünümde CAPTCHA çözümü saniyeler sürebilir; senkron kod worker'ınızı bu süre boyunca meşgul eder. Django 4.1 ve sonrası eşzamansız görünümleri desteklediği için, çözümü aiohttp ile async yürütüp isteği bloklamadan bekleyebilirsiniz:

# views.py (async)
import aiohttp
import asyncio
from django.http import JsonResponse

CAPTCHAAI_API_KEY = "YOUR_API_KEY"

async def solve_captcha_async(request):
    """Async view for solving CAPTCHAs."""
    sitekey = request.GET.get("sitekey")
    page_url = request.GET.get("url")

    if not sitekey or not page_url:
        return JsonResponse({"error": "sitekey and url required"}, status=400)

    async with aiohttp.ClientSession() as session:
        # Submit
        async with session.post("https://ocr.captchaai.com/in.php", data={
            "key": CAPTCHAAI_API_KEY,
            "method": "turnstile",
            "sitekey": sitekey,
            "pageurl": page_url,
            "json": 1,
        }) as resp:
            data = await resp.json()

        if data.get("status") != 1:
            return JsonResponse({"error": data.get("request")}, status=500)

        task_id = data["request"]

        # Poll
        for _ in range(30):
            await asyncio.sleep(5)
            async with session.get("https://ocr.captchaai.com/res.php", params={
                "key": CAPTCHAAI_API_KEY,
                "action": "get",
                "id": task_id,
                "json": 1,
            }) as resp:
                result = await resp.json()

            if result.get("status") == 1:
                return JsonResponse({"token": result["request"]})

    return JsonResponse({"error": "timeout"}, status=504)

Buradaki sorgulama döngüsü en fazla 30 tur (yaklaşık 150 saniye) döner; hedef trafiğinize göre bu üst sınırı ayarlayın.


Celery ile arka planda CAPTCHA çözme

Kullanıcıyı hiç bekletmemek istiyorsanız çözümü tamamen istekten çıkarın: görevi bir Celery kuyruğuna atın, kullanıcıya hemen bir görev kimliği dönün, sonucu ayrı bir uç noktadan sorgulayın. Bu, e-ticaret ve fintech ekiplerinin ödeme veya kayıt akışlarını test ederken en çok tercih ettiği desendir.

# tasks.py
from celery import shared_task
from .services.captcha_solver import CaptchaSolverService, CaptchaSolveError

@shared_task(bind=True, max_retries=2, default_retry_delay=10)
def solve_captcha_task(self, captcha_type, sitekey, page_url):
    """Background CAPTCHA solving with Celery."""
    solver = CaptchaSolverService()

    try:
        if captcha_type == "recaptcha_v2":
            token = solver.solve_recaptcha_v2(sitekey, page_url)
        elif captcha_type == "turnstile":
            token = solver.solve_turnstile(sitekey, page_url)
        else:
            raise ValueError(f"Unknown type: {captcha_type}")

        return {"success": True, "token": token}

    except CaptchaSolveError as e:
        self.retry(exc=e)

Görünüm tarafında görevi başlatır ve durumunu ayrı bir uç noktadan kontrol edersiniz:

# Usage in views
from .tasks import solve_captcha_task

def start_solve(request):
    result = solve_captcha_task.delay("turnstile", "0x4AAA...", "https://example.com")
    return JsonResponse({"task_id": result.id})

def check_solve(request, task_id):
    from celery.result import AsyncResult
    result = AsyncResult(task_id)
    if result.ready():
        return JsonResponse(result.get())
    return JsonResponse({"status": "pending"})

max_retries ve default_retry_delay değerleri, üstel geri çekilme (exponential backoff) mantığını Celery'ye devrederek geçici hatalarda güvenli yeniden deneme sağlar.


Sorun giderme: sık hatalar ve çözümleri

Belirti Sebep Çözüm
Üretimde CaptchaSolveError API anahtarı ayarlarda yok CAPTCHAAI_API_KEY'yi Django ayarlarına ekleyin
Celery görevi sonsuza kadar yeniden deneniyor Çözülemeyen CAPTCHA veya yanlış sitekey max_retries sınırını belirleyin ve girişi doğrulayın
Async görünüm kilitleniyor Async görünümde senkron kod requests yerine aiohttp kullanın
Token, form gönderilmeden süresi doldu Çözüm çok uzun sürdü Önceden değil, tam kullanacağınız anda çözün
Yönetim komutunda import hatası Hizmet INSTALLED_APPS'te değil Uygulama kaydını kontrol edin

Sık sorulan sorular

Formumdaki Turnstile doğrulaması ile CaptchaAI çözümü aynı şey mi?

Hayır, tam tersi yönlerdir. Kendi formunuzda gelen token'ı Cloudflare'e sorarak doğrularsınız (Senaryo 1) ve bunun için CaptchaAI'ye ihtiyaç yoktur. Harici bir sitedeki CAPTCHA'yı geçerli bir token üretmek için CaptchaAI ile çözersiniz (Senaryo 2).

CaptchaAI Django'da hangi CAPTCHA türlerini çözebilir?

Aynı hizmet sınıfıyla reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest v3 ve resim/OCR CAPTCHA'larını çözebilirsiniz. hCaptcha ve FunCaptcha şu an desteklenmiyor; GeeTest v4 ise çok yakında.

Çözülen token ne kadar süre geçerli kalır?

reCAPTCHA token'larının süresi 120 saniyede, Turnstile token'larının süresi 300 saniyede dolar. Bu yüzden token'ı önbelleğe almak pratik değildir; her zaman kullanmadan hemen önce çözün.

CaptchaAI'nin fiyatlandırması nasıl işliyor?

Thread bazlıdır: eşzamanlı CAPTCHA sayısı için ödersiniz, çözüm başına değil. BASIC ($15/ay, 5 thread) ile başlar ve her planda thread başına sınırsız çözüm gelir. Fiyatlar USD'dir, TL kurundan bağımsızdır.

Async view mi Celery mi kullanmalıyım?

Kullanıcı isteği içinde token'a hemen ihtiyacınız varsa async görünüm yeterli. İş uzun sürüyorsa veya isteği hiç bekletmek istemiyorsanız Celery kuyruğuna alın; yönetim komutlarında ise senkron çözüm en sade seçenektir.


Özet

Django uygulamaları CaptchaAI ile, submit/sorgulama akışını saran tek bir hizmet sınıfı üzerinden entegre olur. Yönetim komutlarında senkron çözümü, Django 4.1+ eşzamansız görünümlerinde async çözümü, isteği bloklamadan çalışmak içinse arka planda Celery görevlerini kullanın. Aynı servis reCAPTCHA, Turnstile ve resim CAPTCHA'larını tek elden yönetir; kendi formlarınızı ise Senaryo 1'deki sunucu tarafı doğrulamayla korursunuz.

İlgili makaleler

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