Khi nhiều dịch vụ hoặc thành viên nhóm cần giải CAPTCHA, việc tập trung nó vào một vi dịch vụ sẽ tránh logic trùng lặp giữa các dự án. Khả năng hỗ trợ async của FastAPI khiến nó trở nên rất phù hợp - việc giải CAPTCHA bao gồm việc chờ phản hồi API bên ngoài, async xử lý hiệu quả mà không chặn các luồng.
Hướng dẫn này xây dựng một vi dịch vụ FastAPI chấp nhận các yêu cầu giải CAPTCHA qua REST và trả về mã thông báo đã giải quyết thông qua CaptchaAI.
Những gì bạn cần
| Yêu cầu | Chi tiết |
|---|---|
| Khóa API CaptchaAI | captchaai.com |
| Python 3.9+ | |
| FastAPI + httpx | Để xử lý HTTP không đồng bộ |
Cài đặt phụ thuộc:
pip install fastapi uvicorn httpx
Cấu trúc dự án
captcha-service/
├── main.py # FastAPI app with endpoints
├── solver.py # CaptchaAI solving logic
└── requirements.txt
Mô-đun giải CaptchaAI
# solver.py
import httpx
import asyncio
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://ocr.captchaai.com"
async def submit_task(params: dict) -> str:
"""Submit a CAPTCHA task and return the task ID."""
params["key"] = API_KEY
params["json"] = 1
async with httpx.AsyncClient() as client:
response = await client.post(f"{BASE_URL}/in.php", data=params)
data = response.json()
if data.get("status") != 1:
raise ValueError(f"Submit error: {data.get('request')}")
return data["request"]
async def poll_result(task_id: str, initial_wait: int = 15, max_attempts: int = 30) -> dict:
"""Poll for the CAPTCHA result."""
await asyncio.sleep(initial_wait)
async with httpx.AsyncClient() as client:
for _ in range(max_attempts):
response = await client.get(f"{BASE_URL}/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1
})
data = response.json()
if data.get("status") == 1:
return {
"token": data["request"],
"user_agent": data.get("user_agent", "")
}
if data.get("request") != "CAPCHA_NOT_READY":
raise ValueError(f"Solve error: {data['request']}")
await asyncio.sleep(5)
raise TimeoutError("Solve timed out")
async def solve_recaptcha_v2(sitekey: str, pageurl: str, enterprise: bool = False) -> dict:
params = {"method": "userrecaptcha", "googlekey": sitekey, "pageurl": pageurl}
if enterprise:
params["enterprise"] = 1
task_id = await submit_task(params)
return await poll_result(task_id, initial_wait=20)
async def solve_recaptcha_v3(sitekey: str, pageurl: str, action: str, enterprise: bool = False) -> dict:
params = {
"method": "userrecaptcha", "version": "v3",
"googlekey": sitekey, "pageurl": pageurl, "action": action
}
if enterprise:
params["enterprise"] = 1
task_id = await submit_task(params)
return await poll_result(task_id, initial_wait=20)
async def solve_turnstile(sitekey: str, pageurl: str) -> dict:
task_id = await submit_task({"method": "turnstile", "sitekey": sitekey, "pageurl": pageurl})
return await poll_result(task_id, initial_wait=10)
async def solve_image(image_base64: str) -> dict:
task_id = await submit_task({"method": "base64", "body": image_base64})
return await poll_result(task_id, initial_wait=5, max_attempts=15)
Ứng dụng FastAPI
# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
import solver
app = FastAPI(title="CaptchaAI Solver Service")
class RecaptchaV2Request(BaseModel):
sitekey: str
pageurl: str
enterprise: bool = False
class RecaptchaV3Request(BaseModel):
sitekey: str
pageurl: str
action: str
enterprise: bool = False
class TurnstileRequest(BaseModel):
sitekey: str
pageurl: str
class ImageRequest(BaseModel):
image_base64: str
class SolveResponse(BaseModel):
token: str
user_agent: Optional[str] = ""
@app.post("/solve/recaptcha-v2", response_model=SolveResponse)
async def solve_recaptcha_v2(req: RecaptchaV2Request):
try:
result = await solver.solve_recaptcha_v2(req.sitekey, req.pageurl, req.enterprise)
return SolveResponse(**result)
except (ValueError, TimeoutError) as e:
raise HTTPException(status_code=502, detail=str(e))
@app.post("/solve/recaptcha-v3", response_model=SolveResponse)
async def solve_recaptcha_v3(req: RecaptchaV3Request):
try:
result = await solver.solve_recaptcha_v3(req.sitekey, req.pageurl, req.action, req.enterprise)
return SolveResponse(**result)
except (ValueError, TimeoutError) as e:
raise HTTPException(status_code=502, detail=str(e))
@app.post("/solve/turnstile", response_model=SolveResponse)
async def solve_turnstile(req: TurnstileRequest):
try:
result = await solver.solve_turnstile(req.sitekey, req.pageurl)
return SolveResponse(**result)
except (ValueError, TimeoutError) as e:
raise HTTPException(status_code=502, detail=str(e))
@app.post("/solve/image", response_model=SolveResponse)
async def solve_image(req: ImageRequest):
try:
result = await solver.solve_image(req.image_base64)
return SolveResponse(**result)
except (ValueError, TimeoutError) as e:
raise HTTPException(status_code=502, detail=str(e))
@app.get("/health")
async def health():
return {"status": "ok"}
Chạy dịch vụ
uvicorn main:app --host 0.0.0.0 --port 8000
Ví dụ sử dụng
Giải reCAPTCHA v2
curl -X POST http://localhost:8000/solve/recaptcha-v2 \
-H "Content-Type: application/json" \
-d '{"sitekey": "6Le-wvkS...", "pageurl": "https://staging.example.com/qa-login"}'
Giải Cloudflare Turnstile
curl -X POST http://localhost:8000/solve/turnstile \
-H "Content-Type: application/json" \
-d '{"sitekey": "0x4AAAA...", "pageurl": "https://example.com/form"}'
Trả lời:
{
"token": "03AGdBq24PBCqLmOx2V4...",
"user_agent": "Mozilla/5.0..."
}
Ghi chú tăng cường triển khai
- Đọc khóa API từ môi trường và nhanh chóng bị lỗi trong quá trình khởi động nếu thiếu bí mật.
- Tách biệt việc xác thực yêu cầu khỏi việc thực thi bộ giải để các tải trọng không hợp lệ không bao giờ đến được đường dẫn cuộc gọi đi.
- Trả về các phản hồi lỗi có cấu trúc để phân biệt các lỗi xác thực, lỗi của bộ giải và từ chối ngược dòng.
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Phản hồi 502 | CaptchaAI trả về lỗi | Kiểm tra trường detail để biết lỗi cụ thể |
| Hết thời gian giải quyết | CAPTCHA mất quá nhiều thời gian | Tăng max_attempts hoặc kiểm tra trạng thái CaptchaAI |
| Kết nối bị từ chối | Dịch vụ không chạy | Xác minh uvicorn đang chạy trên cổng dự kiến |
| Phản hồi chậm | Chặn I/O | Đảm bảo httpx.AsyncClient được sử dụng, không phải requests |
Câu hỏi thường gặp
Tại sao nên sử dụng FastAPI cho dịch vụ giải CAPTCHA?
FastAPI xử lý I/O không đồng bộ một cách tự nhiên, điều này lý tưởng cho việc giải CAPTCHA khi phần lớn thời gian được dành để chờ phản hồi của CaptchaAI. Nhiều yêu cầu có thể được xử lý đồng thời mà không cần phân luồng.
Tôi có thể thêm xác thực vào điểm cuối không?
Vâng. Thêm nội dung phụ thuộc của FastAPI bằng xác thực tiêu đề khóa API hoặc OAuth2 để hạn chế quyền truy cập.
Có bao nhiêu yêu cầu đồng thời có thể xử lý được?
Bị giới hạn bởi giới hạn nhiệm vụ đồng thời của gói CaptchaAI của bạn. Bản thân FastAPI có thể xử lý hàng nghìn kết nối đồng thời.
Tôi có nên dockerize cái này không?
Vâng. Thêm Dockerfile với FROM python:3.11-slim, cài đặt các phần phụ thuộc và hiển thị cổng 8000.
Tôi có thể thêm giới hạn tỷ lệ không?
Vâng. Sử dụng slowapi hoặc proxy ngược (nginx, Traefik) để giới hạn yêu cầu trên mỗi khách hàng.
Xây dựng microservice giải CAPTCHA của bạn
Nhận khóa API của bạn tạicaptchaai.com. Tập trung giải quyết CAPTCHA bằng vi dịch vụ FastAPI.
Hướng dẫn liên quan
- Cách giải reCAPTCHA v2 bằng API
- Cách giải Cloudflare Turnstile bằng API
- Không đồng bộ CaptchaAI với aiohttp
- Giải quyết CAPTCHA song song