Khi bạn quản lý việc thu thập dữ liệu hoặc tự động hóa cho nhiều khách hàng, cuối cùng mọi dự án đều đạt được CAPTCHA. Thay vì viết mã giải quyết một lần cho mỗi dự án, hãy xây dựng một quy trình có thể tái sử dụng. Hướng dẫn này đi qua kiến trúc.
Kiến trúc đường ống
┌──────────────┐ ┌───────────────┐ ┌──────────────┐
│ Client A │──▶ │ │ │ │
│ Client B │──▶ │ Task Queue │──▶ │ CaptchaAI │
│ Client C │──▶ │ │ │ API │
└──────────────┘ └───────────────┘ └──────────────┘
│ │
▼ ▼
┌───────────────┐ ┌──────────────┐
│ Result Store │◀── │ Polling │
│ (Redis/DB) │ │ Workers │
└───────────────┘ └──────────────┘
Thành phần:
- Nhận nhiệm vụ - nhận yêu cầu giải quyết từ người dọn dẹp khách hàng
- Hàng đợi — đệm các tác vụ, thực thi các giới hạn đồng thời cho mỗi khách hàng
- Người giải quyết — gửi tới CaptchaAI và thăm dò kết quả
- Cửa hàng kết quả - giữ các mã thông báo đã được giải quyết để người tiêu dùng truy xuất
Đường dẫn Python
Lớp giải quyết cốt lõi
import requests
import time
from dataclasses import dataclass
from typing import Optional
from collections import deque
from threading import Lock
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
@dataclass
class SolveRequest:
client_id: str
method: str
params: dict
callback: Optional[callable] = None
@dataclass
class SolveResult:
client_id: str
task_id: str
token: Optional[str] = None
error: Optional[str] = None
class CaptchaPipeline:
def __init__(self, api_key: str, max_concurrent: int = 10):
self.api_key = api_key
self.max_concurrent = max_concurrent
self.queue = deque()
self.active = {}
self.lock = Lock()
def enqueue(self, request: SolveRequest):
with self.lock:
self.queue.append(request)
def submit_task(self, request: SolveRequest) -> Optional[str]:
data = {
"key": self.api_key,
"method": request.method,
"json": 1,
**request.params
}
try:
resp = requests.post(SUBMIT_URL, data=data, timeout=15)
result = resp.json()
if result.get("status") == 1:
return result["request"]
else:
print(f"[{request.client_id}] Submit error: {result.get('error_text', result.get('request'))}")
return None
except requests.RequestException as e:
print(f"[{request.client_id}] Network error: {e}")
return None
def poll_result(self, task_id: str, max_wait: int = 120) -> Optional[str]:
elapsed = 0
interval = 5
while elapsed < max_wait:
time.sleep(interval)
elapsed += interval
try:
resp = requests.get(RESULT_URL, params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
}, timeout=10)
result = resp.json()
if result.get("status") == 1:
return result["request"]
elif result.get("request") == "CAPCHA_NOT_READY":
continue
else:
print(f"Poll error for {task_id}: {result.get('error_text', result.get('request'))}")
return None
except requests.RequestException:
continue
return None
def process_queue(self):
while self.queue or self.active:
# Fill active slots
with self.lock:
while self.queue and len(self.active) < self.max_concurrent:
request = self.queue.popleft()
task_id = self.submit_task(request)
if task_id:
self.active[task_id] = request
# Poll active tasks
completed = []
for task_id, request in list(self.active.items()):
token = self.poll_result(task_id, max_wait=10)
if token:
result = SolveResult(
client_id=request.client_id,
task_id=task_id,
token=token
)
if request.callback:
request.callback(result)
completed.append(task_id)
with self.lock:
for task_id in completed:
del self.active[task_id]
Sử dụng nhiều khách hàng
pipeline = CaptchaPipeline(api_key="YOUR_API_KEY", max_concurrent=15)
# Client A — reCAPTCHA v2
pipeline.enqueue(SolveRequest(
client_id="client_a",
method="userrecaptcha",
params={
"googlekey": "6Le-SITEKEY-A",
"pageurl": "https://client-a-staging.example.com/qa-form"
},
callback=lambda r: print(f"[{r.client_id}] Solved: {r.token[:40]}...")
))
# Client B — Turnstile
pipeline.enqueue(SolveRequest(
client_id="client_b",
method="turnstile",
params={
"sitekey": "0x4AAAA-SITEKEY-B",
"pageurl": "https://client-b-target.com/login"
},
callback=lambda r: print(f"[{r.client_id}] Solved: {r.token[:40]}...")
))
pipeline.process_queue()
Đường dẫn Node.js
const axios = require("axios");
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
class CaptchaPipeline {
constructor(apiKey, maxConcurrent = 10) {
this.apiKey = apiKey;
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.activeCount = 0;
}
enqueue(clientId, method, params) {
return new Promise((resolve, reject) => {
this.queue.push({ clientId, method, params, resolve, reject });
this._processNext();
});
}
async _processNext() {
if (this.activeCount >= this.maxConcurrent || this.queue.length === 0) return;
this.activeCount++;
const task = this.queue.shift();
try {
const token = await this._solve(task);
task.resolve({ clientId: task.clientId, token });
} catch (err) {
task.reject(err);
} finally {
this.activeCount--;
this._processNext();
}
}
async _solve(task) {
const submitResp = await axios.post(SUBMIT_URL, null, {
params: {
key: this.apiKey,
method: task.method,
json: 1,
...task.params,
},
timeout: 15000,
});
if (submitResp.data.status !== 1) {
throw new Error(submitResp.data.error_text || submitResp.data.request);
}
const taskId = submitResp.data.request;
return this._poll(taskId);
}
async _poll(taskId, maxWait = 120000) {
const interval = 5000;
let elapsed = 0;
while (elapsed < maxWait) {
await new Promise((r) => setTimeout(r, interval));
elapsed += interval;
try {
const resp = await axios.get(RESULT_URL, {
params: {
key: this.apiKey,
action: "get",
id: taskId,
json: 1,
},
timeout: 10000,
});
if (resp.data.status === 1) return resp.data.request;
if (resp.data.request !== "CAPCHA_NOT_READY") {
throw new Error(resp.data.error_text || resp.data.request);
}
} catch (err) {
if (err.response) throw err;
}
}
throw new Error(`Timeout waiting for task ${taskId}`);
}
}
// Usage
(async () => {
const pipeline = new CaptchaPipeline("YOUR_API_KEY", 15);
const results = await Promise.allSettled([
pipeline.enqueue("client_a", "userrecaptcha", {
googlekey: "6Le-SITEKEY-A",
pageurl: "https://client-a-staging.example.com/qa-form",
}),
pipeline.enqueue("client_b", "turnstile", {
sitekey: "0x4AAAA-SITEKEY-B",
pageurl: "https://client-b-target.com/login",
}),
]);
results.forEach((r) => {
if (r.status === "fulfilled") {
console.log(`[${r.value.clientId}] Token: ${r.value.token.slice(0, 40)}...`);
} else {
console.error(`Failed: ${r.reason.message}`);
}
});
})();
Cấu hình cho mỗi khách hàng
Theo dõi cài đặt của mỗi khách hàng như proxy, tùy chọn người giải quyết và giới hạn tỷ lệ:
CLIENT_CONFIG = {
"client_a": {
"proxy": "host:port:user:pass",
"proxytype": "HTTP",
"max_concurrent": 5,
"default_method": "userrecaptcha"
},
"client_b": {
"proxy": None,
"proxytype": None,
"max_concurrent": 10,
"default_method": "turnstile"
}
}
def build_params(client_id, params):
config = CLIENT_CONFIG.get(client_id, {})
if config.get("proxy"):
params["proxy"] = config["proxy"]
params["proxytype"] = config["proxytype"]
return params
Chiến lược xử lý lỗi
| Lỗi | phản hồi |
|---|---|
ERROR_ZERO_BALANCE |
Dừng xếp hàng, cảnh báo tất cả khách hàng |
ERROR_NO_SLOT_AVAILABLE |
Sắp xếp lại nhiệm vụ với độ trễ |
ERROR_WRONG_CAPTCHA_ID |
Hủy, ghi lỗi |
ERROR_CAPTCHA_UNSOLVABLE |
Thử lại một lần rồi thất bại |
| Hết thời gian chờ mạng | Thử lại với thời gian chờ (tối đa 3 lần thử lại) |
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Hàng đợi phát triển không giới hạn | Các khe hoạt động đã đầy | Tăng max_concurrent hoặc thêm công nhân |
| Gọi lại không kích hoạt | Nhiệm vụ thất bại trong âm thầm | Kiểm tra lỗi trả về trong vòng thăm dò ý kiến |
| Mã thông báo hỗn hợp giữa các khách hàng | Kho kết quả được chia sẻ | Kết quả chính của client_id + task_id |
| Lỗi giới hạn tỷ lệ (429) | Quá nhiều lần gửi đồng thời | Giảm đồng thời, thêm độ trễ gửi |
Câu hỏi thường gặp
Tôi nên chạy bao nhiêu tác vụ đồng thời cho mỗi khách hàng?
Bắt đầu với 5–10. Theo dõi thời gian giải quyết và tỷ lệ lỗi, sau đó điều chỉnh. CaptchaAI hỗ trợ tính đồng thời cao nhưng nhóm proxy của bạn có thể là nút cổ chai.
Tôi có nên sử dụng khóa API riêng cho mỗi khách hàng không?
Nó đơn giản hóa việc thanh toán. Sử dụng tham số CaptchaAI soft_id nếu bạn cần theo dõi bằng một phím.
Làm cách nào để xử lý hàng đợi qua đêm?
Duy trì hàng đợi (Redis hoặc cơ sở dữ liệu). Khi khởi động lại, hãy tải lại các tác vụ đang chờ xử lý và tiếp tục xử lý.
Xây dựng quy trình CAPTCHA của bạn với CaptchaAI
Bắt đầu xây dựng quy trình khách hàng tạicaptchaai.com.