Khi một trang có từ hai CAPTCHA trở lên — form đăng nhập dùng reCAPTCHA và ô đăng ký nhận tin dùng reCAPTCHA thứ hai, hoặc một form nhiều bước mà mỗi bước tự bật thử thách riêng — đừng giải tuần tự từng cái một. Quét toàn bộ trang để lấy hết sitekey, gửi tất cả sang CaptchaAI cùng một đợt, rồi gán từng token về đúng widget theo sitekey chứ không theo vị trí trên trang.
Bỏ qua bước gán đúng widget là nguyên nhân phổ biến nhất khiến form vẫn báo lỗi dù token đã được điền — nhất là khi trang trộn nhiều loại CAPTCHA (reCAPTCHA cùng Turnstile) hoặc DOM đổi thứ tự widget sau mỗi lần render lại. Quy trình đúng gồm ba bước:
- Phát hiện: quét trang lấy toàn bộ sitekey và loại CAPTCHA của từng widget đang hiển thị.
- Gửi song song: gửi tất cả task tới CaptchaAI trong cùng một đợt thay vì chờ tuần tự từng cái.
- Gán đúng widget: map từng token về đúng ô theo sitekey, không dùng chỉ số mảng làm khóa.
Lưu ý: invisible reCAPTCHA vẫn có
data-sitekeytrong DOM dù không hiển thị checkbox — lọc theo selector CSS, đừng lọc theo "phần tử đang hiển thị trên màn hình".
Quét toàn bộ CAPTCHA có trên trang
Bắt đầu bằng cách liệt kê toàn bộ widget đang có trên trang, bất kể loại nào — bước quét này dùng chung được cho cả trang chỉ có reCAPTCHA lẫn trang trộn reCAPTCHA với Turnstile (xem phần bên dưới).
Dùng Selenium (Python)
Tìm tất cả iframe reCAPTCHA đang hiển thị, sau đó trích xuất sitekey trực tiếp từ src của từng iframe:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/multi-captcha-page")
# Find all reCAPTCHA iframes
captcha_iframes = driver.find_elements(
By.CSS_SELECTOR, 'iframe[src*="recaptcha/api2/anchor"]'
)
# Extract sitekeys from each iframe's src
import re
captchas = []
for i, iframe in enumerate(captcha_iframes):
src = iframe.get_attribute("src")
match = re.search(r"k=([A-Za-z0-9_-]+)", src)
if match:
captchas.append({
"index": i,
"sitekey": match.group(1),
"iframe": iframe
})
print(f"Found {len(captchas)} CAPTCHAs on page")
for c in captchas:
print(f" [{c['index']}] sitekey: {c['sitekey']}")
Kết quả mong đợi:
Found 2 CAPTCHAs on page
[0] sitekey: 6LcXyzABCDEF-login
[1] sitekey: 6LcAbcDEFGHI-signup
Dùng Puppeteer (JavaScript)
Lấy toàn bộ phần tử .g-recaptcha trên trang rồi đọc data-sitekey và data-callback của từng widget:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/multi-captcha-page', {
waitUntil: 'networkidle2'
});
// Extract all reCAPTCHA widgets from the page
const captchas = await page.evaluate(() => {
const widgets = document.querySelectorAll('.g-recaptcha');
return Array.from(widgets).map((el, i) => ({
index: i,
sitekey: el.getAttribute('data-sitekey'),
elementId: el.id || `captcha-${i}`,
callbackName: el.getAttribute('data-callback') || null
}));
});
console.log(`Found ${captchas.length} CAPTCHAs`);
captchas.forEach(c => console.log(` [${c.index}] ${c.sitekey}`));
})();
Gửi song song tới CaptchaAI và nhận token
Gửi toàn bộ CAPTCHA đã phát hiện tới CaptchaAI trong cùng một đợt, sau đó polling res.php cho tới khi từng task có kết quả.
Số thread đồng thời trên tài khoản phải đủ cho số CAPTCHA bạn gửi cùng lúc — gói BASIC ($15/tháng, 5 thread) là đủ cho hầu hết form có 2-3 CAPTCHA; nếu bạn chạy nhiều phiên song song thì cần cộng dồn thread theo số phiên đang chạy.
Python
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = "YOUR_API_KEY"
PAGE_URL = "https://example.com/multi-captcha-page"
def submit_captcha(sitekey):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": PAGE_URL,
"json": "1"
})
result = resp.json()
if result["status"] != 1:
raise Exception(f"Submit error: {result['request']}")
return result["request"]
def poll_result(task_id, timeout=120):
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1"
})
result = resp.json()
if result["status"] == 1:
return result["request"]
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Poll error: {result['request']}")
raise TimeoutError(f"Task {task_id} timed out")
def solve_all(captchas):
# Submit all in parallel
task_ids = {}
with ThreadPoolExecutor(max_workers=len(captchas)) as pool:
futures = {
pool.submit(submit_captcha, c["sitekey"]): c["index"]
for c in captchas
}
for future in as_completed(futures):
idx = futures[future]
task_ids[idx] = future.result()
print(f"[captcha-{idx}] Submitted → task {task_ids[idx]}")
# Poll all in parallel
tokens = {}
with ThreadPoolExecutor(max_workers=len(task_ids)) as pool:
futures = {
pool.submit(poll_result, tid): idx
for idx, tid in task_ids.items()
}
for future in as_completed(futures):
idx = futures[future]
tokens[idx] = future.result()
print(f"[captcha-{idx}] Solved")
return tokens
# Example usage
captchas = [
{"index": 0, "sitekey": "6LcXyzABCDEF-login"},
{"index": 1, "sitekey": "6LcAbcDEFGHI-signup"}
]
tokens = solve_all(captchas)
Kết quả mong đợi:
[captcha-0] Submitted → task 71823456
[captcha-1] Submitted → task 71823457
[captcha-1] Solved
[captcha-0] Solved
Nếu số CAPTCHA vượt quá số thread khả dụng, task dư sẽ xếp hàng chờ thay vì chạy song song thật. Kiểm tra
max_workerstrongThreadPoolExecutorkhớp với số thread của gói trước khi đo thời gian giải.
Gán token về đúng widget
Mỗi widget reCAPTCHA có vùng g-recaptcha-response riêng, lồng trong container của chính nó. Map theo chỉ số mảng thay vì sitekey là nguyên nhân phổ biến khiến token bị đưa nhầm widget mỗi khi DOM thay đổi.
Selenium (Python)
def inject_tokens(driver, captchas, tokens):
for c in captchas:
idx = c["index"]
token = tokens[idx]
# Find the textarea within the widget's container
container = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")[idx]
textarea = container.find_element(
By.CSS_SELECTOR, 'textarea[name="g-recaptcha-response"]'
)
driver.execute_script(
"arguments[0].value = arguments[1];", textarea, token
)
# Trigger the callback if defined
callback = c.get("callback")
if callback:
driver.execute_script(f"{callback}('{token}');")
print(f"[captcha-{idx}] Token injected")
inject_tokens(driver, captchas, tokens)
Puppeteer (JavaScript)
async function injectTokens(page, captchas, tokens) {
for (const captcha of captchas) {
const token = tokens[captcha.index];
await page.evaluate((idx, tkn, callbackName) => {
const widgets = document.querySelectorAll('.g-recaptcha');
const textarea = widgets[idx].querySelector(
'textarea[name="g-recaptcha-response"]'
);
textarea.value = tkn;
if (callbackName && typeof window[callbackName] === 'function') {
window[callbackName](tkn);
}
}, captcha.index, token, captcha.callbackName);
console.log(`[captcha-${captcha.index}] Token injected`);
}
}
await injectTokens(page, captchas, tokens);
Những lỗi thường gặp khi gán token:
- Widget có
data-callbacknhưng callback không được gọi sau khi set token — form vẫn coi như chưa giải. textareachưa tồn tại trong DOM vì widget invisible chưa render đầy đủ.- Chỉ số mảng lệch sau khi DOM cập nhật động, khiến token gán nhầm widget.
Khi trang có nhiều loại CAPTCHA khác nhau
Khi một trang trộn nhiều loại CAPTCHA (ví dụ reCAPTCHA + Turnstile), phát hiện từng loại riêng trước khi gửi task:
def detect_all_captchas(driver):
detected = []
# reCAPTCHA
recaptchas = driver.find_elements(By.CSS_SELECTOR, ".g-recaptcha")
for i, el in enumerate(recaptchas):
detected.append({
"type": "userrecaptcha",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"recaptcha-{i}"
})
# Turnstile
turnstiles = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile")
for i, el in enumerate(turnstiles):
detected.append({
"type": "turnstile",
"sitekey": el.get_attribute("data-sitekey"),
"label": f"turnstile-{i}"
})
return detected
Gửi kèm đúng tham số method cho từng loại — userrecaptcha cho reCAPTCHA, turnstile cho Turnstile. Trộn nhầm method là lỗi phổ biến khi một trang có cả hai loại.
Tóm tắt ba bước xử lý trang nhiều loại CAPTCHA:
- Phát hiện từng loại bằng selector CSS riêng (
.g-recaptcha,.cf-turnstile). - Gửi mỗi task kèm đúng
methodtương ứng với loại đó. - Gán token về đúng widget theo cả
sitekeylẫn loại, không chỉ theo vị trí trên trang.
Ví dụ thực tế: form đặt lịch hẹn visa nhiều bước (kiểu BLS) thường có reCAPTCHA v2 ở bước xác thực và Turnstile ở bước chọn lịch — cùng phiên, khác bước trên một single-page-app. Đội QA tại các công ty outsourcing ở TP.HCM và Hà Nội hay gặp đúng pattern này khi kiểm thử luồng đăng ký nhiều bước trên staging của chính họ.
Câu hỏi thường gặp
Trang có nhiều CAPTCHA thì mất bao lâu để giải hết?
Không lâu hơn giải một CAPTCHA, miễn là bạn gửi song song. Mỗi lần giải mất khoảng 15–30 giây; gửi 2-3 CAPTCHA cùng lúc tới CaptchaAI gần như không làm tăng tổng thời gian vì các task chạy đồng thời trên các thread riêng.
Cần bao nhiêu thread để giải song song nhiều CAPTCHA?
Tối thiểu bằng số CAPTCHA gửi cùng lúc trong một request; BASIC ($15/tháng, 5 thread) đủ cho hầu hết form. Nhiều phiên song song thì nhân thread mỗi phiên với số phiên đang chạy.
Mỗi CAPTCHA trên trang có sitekey riêng không?
Có. Mỗi widget thường dùng một sitekey khác nhau, đừng giả định chúng dùng chung một sitekey — luôn trích xuất sitekey theo từng widget.
CAPTCHA thứ hai chỉ xuất hiện sau khi submit form đầu tiên thì xử lý sao?
Quét lại trang sau mỗi lần submit để phát hiện widget mới được render. Dùng WebDriverWait (Selenium) hoặc page.waitForSelector (Puppeteer) để đợi widget mới xuất hiện trước khi trích xuất sitekey.
CaptchaAI có tính phí riêng cho từng CAPTCHA trên trang không?
Không. Bạn trả theo thread đang dùng trong gói (ví dụ BASIC $15/tháng, 5 thread), không phải theo số CAPTCHA gửi lên — gửi 2, 3 hay 5 CAPTCHA cùng lúc trong giới hạn thread của gói không phát sinh phí thêm.
Hướng dẫn liên quan
- Trích xuất CAPTCHA trong iframe lồng nhau
- Lấy tham số reCAPTCHA từ mã nguồn trang
- Giải CAPTCHA song song ở quy mô lớn
Lỗi thường gặp và cách xử lý
Nếu token đã có nhưng form vẫn báo lỗi, đối chiếu nhanh với bảng dưới trước khi đào sâu log hoặc network tab:
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Token đã điền nhưng form vẫn báo lỗi | Callback chưa được gọi | Kiểm tra data-callback và gọi hàm đó kèm token |
| Chỉ phát hiện được CAPTCHA đầu tiên | CAPTCHA thứ hai load chậm (lazy) | Đợi toàn bộ iframe/widget xuất hiện rồi mới quét |
| Token bị gán sai widget | Ánh xạ theo chỉ số thay vì sitekey | Map token theo sitekey, không dùng vị trí trong mảng |
ERROR_WRONG_GOOGLEKEY |
Trích xuất sai sitekey | Kiểm tra lại sitekey từ src của iframe hoặc thuộc tính data-sitekey |
Giải mọi CAPTCHA trên trang bằng một API
Lấy API key tại captchaai.com và tích hợp theo các bước ở trên — không giới hạn số CAPTCHA trên mỗi trang.