Aplicações Django frequentemente precisam lidar com CAPTCHAs em dois cenários: verificando CAPTCHAs em seus próprios formulários (proteção contra bots) e resolvendo CAPTCHAs em sites externos (coleta de dados, testes, automação). Este guia cobre ambos os padrões usando CaptchaAI.
Cenário 1: Verificando CAPTCHAs em seus formulários Django
Ao adicionar Turnstile ou reCAPTCHA aos seus formulários Django, você precisa verificar os tokens no lado do servidor.
Adicionando Turnstile a um formulário Django
# 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,
)
# 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,
})
<!-- 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>
Cenário 2: Resolvendo CAPTCHAs em sites externos
É aqui que entra o CaptchaAI — quando seu aplicativo Django precisa interagir com sites externos protegidos por CAPTCHA.
Classe de serviço CaptchaAI
# 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
Configurações do Django
# settings.py
CAPTCHAAI_API_KEY = "YOUR_API_KEY"
TURNSTILE_SITE_KEY = "0x4AAAAAAAC3DHQhMMQ_Rxrg"
TURNSTILE_SECRET_KEY = "0x4AAAAAAAC3DHQhYYY_secret"
Usando o serviço em visualizações
Visualização para coleta de dados externos
# 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)
Comando de gerenciamento Django
# 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}")
Uso:
python manage.py solve_captcha --type turnstile --sitekey 0x4AAA... --url https://example.com
Django assíncrono com CaptchaAI
Django 4.1+ suporta visualizações assíncronas:
# 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)
Integração do Celery para resolução em segundo plano
Para soluções CAPTCHA de longa duração, use Celery:
# 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)
# 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"})
Solução de problemas
| Sintoma | Causa | Correção |
|---|---|---|
CaptchaSolveError em produção |
Chave de API não está nas configurações | Adicione CAPTCHAAI_API_KEY às configurações do Django |
| A tarefa do Celery é repetida indefinidamente | CAPTCHA insolúvel ou chave de site errada | Defina max_retries e valide a entrada |
| A visualização assíncrona trava | Sincronizar código na visualização assíncrona | Use aiohttp em vez de requests |
| O token expirou antes do envio do formulário | Resolver demorou muito | Resolva just-in-time, não antecipadamente |
| Erros de importação no comando de gerenciamento | Serviço não em INSTALLED_APPS | Verifique o registro do aplicativo |
Perguntas frequentes
A resolução de CAPTCHA deve ser síncrona ou assíncrona?
Use o Celery para visualizações voltadas para a web para que o usuário não espere mais de 15 segundos. Use resolução síncrona em comandos de gerenciamento e scripts em segundo plano.
Como posso armazenar chaves de API com segurança?
Use variáveis de ambiente ou o pacote django-environ do Django. Nunca confirme chaves de API para controle de versão.
Posso armazenar tokens resolvidos em cache?
Os tokens reCAPTCHA expiram em 120 segundos e os tokens Turnstile em 300 segundos. O cache não é prático – resolva logo antes de usar.
Devo criar uma instância de serviço ou usar um singleton?
A classe CaptchaSolverService não tem estado. Crie uma nova instância por solicitação ou use os padrões de injeção de dependência do Django.
Resumo
Aplicativos Django integram-se comCaptchaAIpor meio de uma classe de serviço que envolve o fluxo submit/poll. Use resolução síncrona em comandos de gerenciamento, resolução assíncrona em visualizações assíncronas do Django 4.1+ e tarefas do Celery para processamento em segundo plano. O mesmo serviço lida com reCAPTCHA, Turnstile e CAPTCHAs de imagem.
Artigos relacionados
- Comparação de Geetest vs Cloudflare Turnstile
- Cloudflare Turnstile 403 Após correção do token
- Modos de widget Cloudflare Turnstile explicados