Dưới 500 CAPTCHA/ngày, giải tuần tự là đủ và code đơn giản hơn nhiều; vượt ngưỡng đó, song song tăng thông lượng gấp hàng chục lần, đổi lại là lỗi đồng thời và debug khó hơn. Bài này so sánh trực tiếp hai cách với CaptchaAI, kèm code Python và JavaScript chạy được ngay.
Script tuần tự đang mất vài phút để xử lý 100 CAPTCHA? Phần dưới đây chỉ ra chỗ nên chuyển sang song song — và chỗ chưa cần.
Chọn nhanh: tuần tự hay song song
- Dưới 500 giải/ngày, hoặc đang debug → chọn tuần tự.
- Trên 500 giải/ngày, pipeline đã ổn định → chuyển sang song song.
- Kết quả phải dùng đúng thứ tự phát sinh → tuần tự, hoặc song song kèm index tracking.
- Container nhỏ, ít RAM (dưới 256 MB) → tuần tự, hoặc giới hạn
max_concurrentthấp.
Giải tuần tự: khi mã đơn giản quan trọng hơn tốc độ
Cách hoạt động
Từng CAPTCHA một: gửi task, đợi, polling res.php đến khi có kết quả, rồi mới sang task tiếp theo. Đoạn code dưới gửi 10 task tuần tự — hết khoảng 150 giây vì mỗi lượt phải chờ lượt trước hoàn tất.
# sequential_solver.py
import os
import time
import requests
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
def solve_sequential(tasks):
"""Solve CAPTCHAs one by one."""
results = []
session = requests.Session()
for task in tasks:
# Submit
resp = session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": task["sitekey"],
"pageurl": task["pageurl"],
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
results.append({"error": result.get("request")})
continue
task_id = result["request"]
time.sleep(15)
# Poll
token = None
for _ in range(25):
poll = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
})
poll_result = poll.json()
if poll_result.get("status") == 1:
token = poll_result["request"]
break
if poll_result.get("request") != "CAPCHA_NOT_READY":
break
time.sleep(5)
results.append({"token": token} if token else {"error": "timeout"})
return results
# 10 tasks sequentially → ~150 seconds total
tasks = [{"sitekey": "SITEKEY", "pageurl": "https://example.com"}] * 10
start = time.time()
results = solve_sequential(tasks)
print(f"Completed in {time.time() - start:.0f}s")
Ưu điểm
Log lỗi rõ ràng, dễ trace; không cần race condition hay cô lập trạng thái; thứ tự kết quả tự động đúng.
Giải song song: tăng thông lượng bằng asyncio và Promise.all
Cách hoạt động với Python
Gửi toàn bộ CAPTCHA gần như cùng lúc, polling đồng thời qua asyncio.gather. Semaphore là van an toàn — giới hạn số task chạy song song, tránh tràn API và bị trả lỗi.
# parallel_solver.py
import os
import asyncio
import aiohttp
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
async def solve_one(session, sitekey, pageurl, semaphore):
"""Solve a single CAPTCHA within concurrency limits."""
async with semaphore:
# Submit
async with session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY, "method": "userrecaptcha",
"googlekey": sitekey, "pageurl": pageurl, "json": "1",
}) as resp:
result = await resp.json(content_type=None)
if result.get("status") != 1:
return {"error": result.get("request")}
task_id = result["request"]
await asyncio.sleep(15)
# Poll
for _ in range(25):
async with session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
}) as resp:
poll_result = await resp.json(content_type=None)
if poll_result.get("status") == 1:
return {"token": poll_result["request"]}
if poll_result.get("request") != "CAPCHA_NOT_READY":
return {"error": poll_result.get("request")}
await asyncio.sleep(5)
return {"error": "timeout"}
async def solve_parallel(tasks, max_concurrent=50):
"""Solve CAPTCHAs in parallel with concurrency control."""
semaphore = asyncio.Semaphore(max_concurrent)
connector = aiohttp.TCPConnector(limit=max_concurrent)
async with aiohttp.ClientSession(connector=connector) as session:
coros = [
solve_one(session, t["sitekey"], t["pageurl"], semaphore)
for t in tasks
]
return await asyncio.gather(*coros)
# 10 tasks in parallel → ~20 seconds total
import time
tasks = [{"sitekey": "SITEKEY", "pageurl": "https://example.com"}] * 10
start = time.time()
results = asyncio.run(solve_parallel(tasks))
print(f"Completed in {time.time() - start:.0f}s")
Ví dụ JavaScript
Cùng logic, viết bằng axios và Promise.all — phù hợp nếu stack của bạn là Node.js thay vì Python.
// parallel_solver.js
const axios = require('axios');
const https = require('https');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const agent = new https.Agent({ keepAlive: true, maxSockets: 50 });
const api = axios.create({ baseURL: 'https://ocr.captchaai.com', httpsAgent: agent });
async function solveOne(sitekey, pageurl) {
const submit = await api.get('/in.php', {
params: { key: API_KEY, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: '1' },
});
if (submit.data.status !== 1) return { error: submit.data.request };
await new Promise(r => setTimeout(r, 15000));
for (let i = 0; i < 25; i++) {
const poll = await api.get('/res.php', {
params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
});
if (poll.data.status === 1) return { token: poll.data.request };
if (poll.data.request !== 'CAPCHA_NOT_READY') return { error: poll.data.request };
await new Promise(r => setTimeout(r, 5000));
}
return { error: 'timeout' };
}
(async () => {
const tasks = Array.from({ length: 10 }, () => ({
sitekey: 'SITEKEY', pageurl: 'https://example.com',
}));
const start = Date.now();
const results = await Promise.all(tasks.map(t => solveOne(t.sitekey, t.pageurl)));
console.log(`Completed in ${((Date.now() - start) / 1000).toFixed(0)}s`);
console.log(`Solved: ${results.filter(r => r.token).length}/${tasks.length}`);
agent.destroy();
})();
Ví dụ thực tế: kết hợp cả hai cách trong một pipeline
Không cần chọn hẳn một phe: giữ luồng nghiệp vụ tuần tự nhưng gom riêng phần giải CAPTCHA thành batch và chạy song song.
Một đội thu thập giá sản phẩm trên Shopee và Tiki cho một công ty outsource ở Hà Nội xử lý khoảng 2.000 URL mỗi đêm. Trích sitekey vẫn chạy tuần tự vì dễ log lỗi theo URL; giải CAPTCHA thì gom batch 20 tác vụ chạy song song — rút thời gian xử lý cả lô từ 8 giờ xuống dưới 1 giờ.
# Process 10 URLs sequentially, but solve their CAPTCHAs in a parallel batch
urls = get_next_batch() # 10 URLs
captcha_params = [extract_sitekey(url) for url in urls] # Sequential extraction
tokens = asyncio.run(solve_parallel(captcha_params, max_concurrent=10)) # Parallel solving
for url, result in zip(urls, tokens):
submit_form(url, result.get("token")) # Sequential submission
Thông lượng theo mức đồng thời
| Số lượng giải đồng thời | Thông lượng ước tính/giờ | Bộ nhớ (Python) | Độ phức tạp |
|---|---|---|---|
| 1 (tuần tự) | 240 | 30 MB | Thấp |
| 10 | 2.400 | 50 MB | Thấp |
| 25 | 6.000 | 80 MB | Trung bình |
| 50 | 10.000+ | 120 MB | Trung bình |
| 100 | 18.000+ | 200 MB | Cao |
Dựa trên reCAPTCHA v2 với thời gian giải trung bình 15 giây.
Khi nào nên chuyển hẳn sang song song
- Thông lượng hiện tại không theo kịp khối lượng phát sinh mỗi giờ.
- Máy chủ đủ RAM — mỗi 50 tác vụ đồng thời cần thêm khoảng 120 MB.
- Bạn sẵn sàng thêm cô lập lỗi và theo dõi thứ tự nếu logic cần.
- Chi phí API không đổi vì CaptchaAI tính theo thread, không theo request đồng thời.
Xử lý sự cố thường gặp
Bốn lỗi hay gặp nhất khi chuyển sang song song:
- Chậm hơn dự kiến — semaphore giới hạn quá chặt; tăng
max_concurrent. - Lỗi ngẫu nhiên chỉ khi chạy song song — trạng thái dùng chung bị ghi đè; cô lập trạng thái riêng cho từng coroutine/promise.
- Kết quả sai thứ tự — hiếm gặp vì
asyncio.gathergiữ đúng thứ tự; nếu cần, dùng index tracking. ERROR_NO_SLOT_AVAILABLE— gửi quá nhiều task cùng lúc; thêm độ trễ nhỏ hoặc giảmmax_concurrent.
Câu hỏi thường gặp
Bốn câu hay gặp khi cân nhắc chuyển sang song song.
Chuyển từ tuần tự sang song song có phải viết lại toàn bộ code không?
Không, nếu logic giải đã được tách thành một hàm độc lập — chỉ cần gói lời gọi trong asyncio.gather (Python) hoặc Promise.all (JavaScript).
Chạy 50 tác vụ song song cần bao nhiêu RAM?
Khoảng 120 MB với Python. Container nhỏ (dưới 256 MB) nên giới hạn max_concurrent ở 25.
Giải song song có cần nâng lên gói nhiều thread hơn không?
Có liên quan nhưng không bắt buộc. CaptchaAI tính phí theo thread, giải không giới hạn trên mỗi thread — số thread trong gói quyết định mức đồng thời tối đa, không quyết định chi phí mỗi lần giải. Gói BASIC ($15/tháng, 5 thread) vẫn chạy song song được, chỉ giới hạn 5 tác vụ cùng lúc.
Đồng thời thực tế nên dừng ở mức nào?
Phần lớn hệ thống đạt lợi nhuận giảm dần sau khoảng 100 tác vụ đồng thời — nên tối ưu tham số giải thay vì tăng thêm concurrency.
Bước tiếp theo
Chọn chiến lược đúng với khối lượng thực tế của bạn — bắt đầu tuần tự nếu đang debug, chuyển sang song song khi vượt 500 giải/ngày. Lấy API key CaptchaAI và áp dụng ngay ví dụ Python hoặc JavaScript ở trên.