reCAPTCHA vô hình thường "im lặng" khi hỏng: bạn bấm gửi, biểu mẫu không đi đâu cả và cũng chẳng có thông báo nào. Bỏ đi checkbox không có nghĩa là bớt lỗi — nó chỉ đổi sang một nhóm lỗi khó soi hơn. Trong phần lớn trường hợp, nguyên nhân rơi đúng vào bốn chỗ:
- scraper không nhận ra widget vô hình trên trang;
- quên gọi hàm callback sau khi chèn token;
- gửi token đã hết hạn quá 120 giây;
- thiếu tham số
invisible=1khi gọi CaptchaAI.
Bài viết đi qua từng mẫu lỗi kèm cách khắc phục chính xác và code chạy được ngay. Nếu bạn muốn nắm cơ chế trước khi gỡ lỗi, hãy đọc reCAPTCHA vô hình hoạt động thế nào và cách giải rồi quay lại đây. Còn nếu đang cần sửa gấp, hãy bắt đầu từ bảng tra và bảng chẩn đoán theo triệu chứng ngay bên dưới.
Bảng tra lỗi nhanh
| Lỗi | Nguyên nhân | Cách khắc phục |
|---|---|---|
ERROR_WRONG_GOOGLEKEY |
Sai sitekey, hoặc sitekey lấy từ một tên miền khác | Lấy sitekey từ div widget vô hình hoặc lời gọi grecaptcha.render() |
ERROR_PAGEURL |
URL không khớp — gửi URL trang cha thay vì URL iframe | Dùng đúng URL nơi widget vô hình được tải |
ERROR_CAPTCHA_UNSOLVABLE |
Google đánh dấu task là không giải được | Thử lại với proxy và cookie mới; kiểm tra xem trang đã chuyển sang v3 chưa |
ERROR_BAD_TOKEN_OR_PAGEURL |
Token bị trang đích từ chối | Kiểm tra pageurl khớp chính xác; chèn qua callback, không phải trường ẩn |
CAPCHA_NOT_READY |
Task vẫn đang xử lý | Tiếp tục polling mỗi 5 giây; giải vô hình mất 10–30 giây |
ERROR_KEY_DOES_NOT_EXIST |
API key CaptchaAI không hợp lệ | Kiểm tra key tại captchaai.com/account |
| Token được chấp nhận nhưng biểu mẫu vẫn lỗi | Chưa gọi callback sau khi chèn token | Tìm và gọi hàm trong data-callback với token |
Chẩn đoán nhanh theo triệu chứng
Trước khi lật từng mã lỗi, hãy đối chiếu triệu chứng bạn đang thấy với nguyên nhân hay gặp nhất. Bảng dưới giúp bạn nhảy thẳng tới phần cần đọc thay vì dò tuần tự:
| Triệu chứng bạn thấy | Khả năng cao nhất | Phần nên đọc |
|---|---|---|
| Không thấy CAPTCHA nào nhưng request vẫn bị chặn | scraper không nhận ra widget vô hình | "Bỏ sót widget reCAPTCHA vô hình" |
| Token nhận được nhưng bấm gửi thì trang đứng im | chưa gọi callback | "Callback không được gọi" |
CaptchaAI trả ERROR_CAPTCHA_UNSOLVABLE liên tục |
thiếu invisible=1 |
"Thiếu tham số invisible=1" |
| Hoạt động lúc test, hỏng khi chạy thật | token hết hạn hoặc IP lệch | "Token hết hạn" và "Token bị từ chối" |
siteverify trả timeout-or-duplicate |
token hết hạn hoặc bị dùng lại | "Token hết hạn trước khi kịp gửi" |
Checklist khắc phục khi giải thất bại
Chạy lần lượt qua checklist này mỗi khi một ca giải reCAPTCHA vô hình thất bại:
| Bước | Cần kiểm tra | Lệnh/Thao tác |
|---|---|---|
| 1 | Xác nhận đúng là vô hình, không phải v2 tiêu chuẩn | Tìm data-size="invisible" hoặc size: 'invisible' trong lời gọi render |
| 2 | Kiểm tra sitekey có đúng không | So sánh với data-sitekey của riêng widget vô hình |
| 3 | Chắc chắn có invisible=1 trong request API |
Soi lại tham số in.php |
| 4 | pageurl khớp chính xác |
Dùng URL từ DevTools trình duyệt, không phải URL sau chuyển hướng |
| 5 | Tìm tên hàm callback | Tìm thuộc tính data-callback hoặc callback trong grecaptcha.render() |
| 6 | Đã chèn token VÀ gọi callback chưa | Cả hai bước đều bắt buộc — chỉ mình token là chưa đủ |
| 7 | Kiểm tra độ mới của token | Token phải được dùng trong vòng 120 giây |
| 8 | Thử với proxy nếu nghi vấn IP | Thêm tham số proxy và proxytype |
Bỏ sót widget reCAPTCHA vô hình trên trang
reCAPTCHA vô hình không hiển thị checkbox nào. Nếu scraper của bạn không nhận ra nó, các request bị bảo vệ sẽ thất bại lặng lẽ. Các dấu hiệu thường thấy:
- biểu mẫu gửi đi nhưng bị trả về trang cũ, không kèm thông báo;
- server đáp lỗi chung chung hoặc bị chuyển hướng sang trang đăng nhập;
- không có ô CAPTCHA nào hiển thị nên dễ tưởng nhầm là trang không hề bảo vệ.
Cách nhận diện trong HTML. Tìm một trong ba mẫu sau trong HTML của trang:
<!-- Pattern 1: div with data-size="invisible" -->
<div class="g-recaptcha" data-sitekey="6LdKlZEU..."
data-size="invisible"
data-callback="onSubmit"></div>
<!-- Pattern 2: button with data-sitekey and invisible size -->
<button class="g-recaptcha"
data-sitekey="6LdKlZEU..."
data-callback="onSubmit"
data-action="submit">Submit</button>
<!-- Pattern 3: programmatic render with size: invisible -->
<script>
grecaptcha.render('submit-btn', {
sitekey: '6LdKlZEU...',
callback: onSubmit,
size: 'invisible'
});
</script>
Script nhận diện (Python):
import requests
from bs4 import BeautifulSoup
import re
def detect_invisible_recaptcha(url):
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
# Check for data-size="invisible"
widget = soup.find("div", {"data-size": "invisible", "class": "g-recaptcha"})
if widget:
return {
"type": "invisible",
"sitekey": widget.get("data-sitekey"),
"callback": widget.get("data-callback")
}
# Check for programmatic render with invisible
scripts = soup.find_all("script")
for script in scripts:
if script.string and "size" in str(script.string) and "invisible" in str(script.string):
key_match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", script.string)
if key_match:
return {
"type": "invisible-programmatic",
"sitekey": key_match.group(1),
"callback": "check grecaptcha.render() call"
}
return None
Script nhận diện (Node.js):
const axios = require("axios");
const cheerio = require("cheerio");
async function detectInvisibleRecaptcha(url) {
const { data } = await axios.get(url);
const $ = cheerio.load(data);
// Check for data-size="invisible"
const widget = $(".g-recaptcha[data-size='invisible']");
if (widget.length) {
return {
type: "invisible",
sitekey: widget.attr("data-sitekey"),
callback: widget.attr("data-callback"),
};
}
// Check script tags for programmatic invisible render
const scriptContent = $("script")
.map((_, el) => $(el).html())
.get()
.join("\n");
if (scriptContent.includes("invisible")) {
const keyMatch = scriptContent.match(/sitekey['"]?\s*[:=]\s*['"]([^'"]+)/);
if (keyMatch) {
return {
type: "invisible-programmatic",
sitekey: keyMatch[1],
callback: "check grecaptcha.render() call",
};
}
}
return null;
}
Sai sitekey — ERROR_WRONG_GOOGLEKEY
Lỗi này xuất hiện khi sitekey bạn gửi không khớp với widget reCAPTCHA vô hình trên trang đích. Các nguyên nhân hay gặp:
- Copy nhầm sitekey từ một widget v2 checkbox ở trang khác
- Dùng sitekey lấy từ anchor URL của một phiên bản reCAPTCHA khác
- Trang có nhiều widget reCAPTCHA và bạn lấy nhầm cái
Cách khắc phục: lấy đúng sitekey của widget vô hình
import requests
from bs4 import BeautifulSoup
def get_invisible_sitekey(url):
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
# Priority 1: invisible widget
widget = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
if widget:
return widget["data-sitekey"]
# Priority 2: any g-recaptcha div (may be invisible without data-size)
widget = soup.find(class_="g-recaptcha")
if widget and widget.get("data-sitekey"):
return widget["data-sitekey"]
return None
sitekey = get_invisible_sitekey("https://staging.example.com/qa-login")
print(f"Sitekey: {sitekey}")
Callback không được gọi — bấm gửi nhưng trang đứng im
Đây là lỗi reCAPTCHA vô hình phổ biến nhất mà dev hay bỏ sót. Điểm khác biệt nằm ở bước cuối cùng:
- v2 checkbox: chèn token vào
g-recaptcha-responselà biểu mẫu chạy được; - reCAPTCHA vô hình: chèn token xong, bạn còn phải tự gọi hàm callback JavaScript thì biểu mẫu mới xử lý.
Chèn token mà quên callback thì biểu mẫu đứng im mãi mãi.
Luồng callback hoạt động thế nào
grecaptcha.execute()kích hoạt thử thách vô hình- Giải xong, Google gọi hàm được khai báo trong
data-callback - Hàm callback đó mới thật sự gửi biểu mẫu hoặc gọi API
Cách khắc phục: tìm và tự gọi callback
Tên callback thường nằm ở một trong ba nơi:
- thuộc tính
data-callbacktrên thẻ widget; - tham số
callbacktrong lời gọigrecaptcha.render(); - hàm được truyền vào
grecaptcha.execute()trong mã trang.
Bước 1 — xác định tên hàm callback:
# From HTML: data-callback="onSubmit"
# From JS: callback: onSubmit
# From grecaptcha.render: second argument with callback property
Bước 2 — chèn token VÀ gọi callback (Selenium):
from selenium import webdriver
import requests
import time
driver = webdriver.Chrome()
driver.get("https://example.com/form")
# Get sitekey
sitekey = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-sitekey")
callback_name = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")
# Solve with CaptchaAI
task_id = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": driver.current_url,
"invisible": 1
}).text.split("|")[1]
# Poll for result
token = None
for _ in range(60):
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY",
"action": "get",
"id": task_id
}).text
if resp.startswith("OK|"):
token = resp.split("|")[1]
break
# Inject token into the response field
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# CRITICAL: Call the callback function
driver.execute_script(f'{callback_name}("{token}");')
Bước 2 — chèn token VÀ gọi callback (Puppeteer):
const puppeteer = require("puppeteer");
const axios = require("axios");
(async () => {
const browser = await puppeteer.launch({ headless: "new" });
const page = await browser.newPage();
await page.goto("https://example.com/form");
// Get sitekey and callback
const { sitekey, callback } = await page.evaluate(() => {
const el = document.querySelector(".g-recaptcha[data-size='invisible']");
return {
sitekey: el?.getAttribute("data-sitekey"),
callback: el?.getAttribute("data-callback"),
};
});
// Submit to CaptchaAI
const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: "YOUR_API_KEY",
method: "userrecaptcha",
googlekey: sitekey,
pageurl: page.url(),
invisible: 1,
},
});
const taskId = submitResp.data.split("|")[1];
// Poll for result
let token;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: "YOUR_API_KEY", action: "get", id: taskId },
});
if (result.data.startsWith("OK|")) {
token = result.data.split("|")[1];
break;
}
}
// Inject token and fire callback
await page.evaluate(
(tok, cb) => {
document.getElementById("g-recaptcha-response").value = tok;
if (cb && typeof window[cb] === "function") {
window[cb](tok);
}
},
token,
callback,
);
await browser.close();
})();
Thiếu tham số invisible=1 khi gọi API
Khi giải reCAPTCHA vô hình qua CaptchaAI, bạn bắt buộc thêm invisible=1 vào request. Thiếu tham số này, hệ thống sẽ xử lý task như một v2 checkbox tiêu chuẩn. Hậu quả thường thấy:
- trả về
ERROR_CAPTCHA_UNSOLVABLEvì xử lý sai loại thử thách; - token có vẻ hợp lệ nhưng trang đích từ chối khi verify;
- tỷ lệ giải thành công tụt hẳn dù sitekey vẫn đúng.
So sánh request thiếu và request đủ tham số invisible=1:
# WRONG — missing invisible=1
params = {
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url
}
# CORRECT — includes invisible=1
params = {
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1 # Required for invisible reCAPTCHA
}
response = requests.get("https://ocr.captchaai.com/in.php", params=params)
Token hết hạn trước khi kịp gửi
Token reCAPTCHA vô hình hết hạn sau 120 giây, đúng bằng v2 tiêu chuẩn. Điều khiến bản vô hình dễ dính lỗi hết hạn hơn:
- luồng vô hình thường chèn thêm bước giữa lúc giải và lúc gửi;
- callback có thể phải chờ một thao tác khác trên trang rồi mới chạy;
- khi gom nhiều biểu mẫu, token giải sớm phải nằm chờ khá lâu.
Đây là tình huống hay gặp ở các team QA tại công ty outsourcing ở TP.HCM hay Hà Nội: một job kiểm thử đăng nhập chạy hàng đêm giải cả loạt token cùng lúc rồi mới lần lượt gửi từng biểu mẫu — đến biểu mẫu thứ ba, thứ tư thì token đầu tiên đã quá 120 giây và fail mà log không nói rõ lý do. Cách sửa gọn nhất là giải riêng cho từng lần gửi, ngay sát thời điểm gửi.
Dấu hiệu thường thấy:
- Biểu mẫu báo lỗi chung chung sau khi chèn token
siteverifyphía server trả vềtimeout-or-duplicate- Token vốn hợp lệ nhưng mất quá lâu mới tới được bước gửi
Cách khắc phục: giải sát thời điểm gửi (just-in-time)
Chỉ gọi giải khi bạn đã sẵn sàng gửi ngay lập tức:
import requests
import time
def solve_invisible_recaptcha(api_key, sitekey, page_url):
# Submit task
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1
})
if not resp.text.startswith("OK|"):
raise Exception(f"Submit failed: {resp.text}")
task_id = resp.text.split("|")[1]
# Poll for result
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key,
"action": "get",
"id": task_id
})
if result.text.startswith("OK|"):
return result.text.split("|")[1]
if result.text != "CAPCHA_NOT_READY":
raise Exception(f"Solve failed: {result.text}")
raise Exception("Solve timed out after 5 minutes")
# Usage: solve JUST before you need to submit
# 1. Navigate to page and prepare form data first
# 2. THEN solve the captcha
# 3. Inject token and submit immediately
token = solve_invisible_recaptcha("YOUR_API_KEY", sitekey, page_url)
# Submit within 120 seconds of receiving the token
Token bị từ chối — ERROR_BAD_TOKEN_OR_PAGEURL
Trang đích đã gửi token cho Google xác minh và nhận về kết quả thất bại. Các nguyên nhân thường gặp:
| Nguyên nhân | Cách xác định | Cách khắc phục |
|---|---|---|
Sai pageurl |
URL không khớp tên miền đã đăng ký sitekey | Dùng đúng URL nơi widget được tải |
| Token dùng trên tên miền khác | Tái sử dụng token giữa các tên miền | Giải lại với pageurl đúng tên miền |
| Token đã được dùng | Gửi cùng một token hai lần | Xin token mới cho mỗi lần gửi |
| IP không khớp | IP của bạn khác IP của bên giải | Thêm tham số proxy để khớp IP phiên |
| Thiếu cờ vô hình | Giải theo v2 tiêu chuẩn nhưng dùng trên trang vô hình | Thêm invisible=1 vào request giải |
Script chẩn đoán dưới đây in ra từng bước để khoanh vùng nguyên nhân:
def debug_invisible_solve(api_key, sitekey, page_url, proxy=None):
"""Run a diagnostic solve with detailed logging."""
print(f"Sitekey: {sitekey}")
print(f"Page URL: {page_url}")
print(f"Proxy: {proxy or 'none'}")
params = {
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1
}
if proxy:
params["proxy"] = proxy
params["proxytype"] = "HTTP"
# Submit
resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
print(f"Submit response: {resp.text}")
if not resp.text.startswith("OK|"):
return None
task_id = resp.text.split("|")[1]
print(f"Task ID: {task_id}")
# Poll with timing
start = time.time()
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get", "id": task_id
})
elapsed = time.time() - start
print(f" [{elapsed:.0f}s] {result.text[:50]}")
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
print(f"Token received after {elapsed:.0f}s")
print(f"Token length: {len(token)} characters")
print(f"Token starts with: {token[:30]}...")
return token
if result.text != "CAPCHA_NOT_READY":
print(f"FAILED: {result.text}")
return None
print("TIMEOUT after 5 minutes")
return None
Nhiều widget reCAPTCHA trên cùng một trang
Một số trang đặt cả v2 checkbox hiển thị LẪN reCAPTCHA vô hình. Giải nhầm widget thì token vẫn hợp lệ nhưng không khớp với widget đang bảo vệ hành động bạn cần. Dấu hiệu nhận biết trang có nhiều widget:
- vừa có ô v2 checkbox hiển thị, vừa có nút gửi chạy ngầm;
- nhiều thẻ
g-recaptchavớidata-sitekeykhác nhau trong cùng HTML; - mỗi biểu mẫu (đăng nhập, đăng ký) gắn một widget riêng.
Cách khắc phục: nhắm đúng widget
from bs4 import BeautifulSoup
def find_all_recaptcha_widgets(html):
soup = BeautifulSoup(html, "html.parser")
widgets = []
for el in soup.find_all(class_="g-recaptcha"):
widgets.append({
"sitekey": el.get("data-sitekey"),
"size": el.get("data-size", "normal"),
"callback": el.get("data-callback"),
"tag": el.name,
"id": el.get("id")
})
return widgets
# Example output:
# [
# {"sitekey": "6LdA...", "size": "normal", "callback": None, "tag": "div", "id": "recaptcha-login"},
# {"sitekey": "6LdB...", "size": "invisible", "callback": "onRegister", "tag": "div", "id": "recaptcha-register"}
# ]
# Use the widget with size="invisible" for the invisible solve
Câu hỏi thường gặp
Vì sao token giải đúng nhưng biểu mẫu vẫn không gửi được?
Gần như chắc chắn bạn chưa gọi hàm callback. reCAPTCHA vô hình không xử lý biểu mẫu chỉ vì trường g-recaptcha-response đã có token — nó chờ hàm khai báo trong data-callback được gọi với token đó. Hãy đọc tên callback từ HTML rồi tự gọi nó ngay sau khi chèn token, như hướng dẫn ở phần callback bên trên.
reCAPTCHA vô hình mất bao lâu để giải qua CaptchaAI?
Thường 10–30 giây. Thử thách vô hình nhìn chung nhanh hơn v2 checkbox vì không cần nhận dạng hình ảnh — nó dựa trên phân tích rủi ro hành vi. Trong lúc chờ, cứ polling res.php mỗi 5 giây cho tới khi nhận về OK|.
Lỗi timeout-or-duplicate khi verify token nghĩa là gì?
Nghĩa là token đã hết hạn (quá 120 giây) hoặc đã bị dùng một lần trước đó. Xin một token mới cho mỗi lần gửi và chỉ giải ngay sát thời điểm gửi để tránh lỗi này; đừng tái sử dụng lại một token cho hai request.
Giải reCAPTCHA vô hình có đắt hơn v2 checkbox không?
Không. CaptchaAI tính tiền theo thread (luồng giải đồng thời), không tính theo từng lần giải hay theo loại CAPTCHA. Gói BASIC ($15/tháng, 5 thread) giải reCAPTCHA vô hình, v2 và các loại được hỗ trợ khác với cùng một mức giá; bạn chỉ nâng gói khi cần thêm thread để chạy song song nhiều hơn.
Bộ giải reCAPTCHA vô hình hoàn chỉnh kèm xử lý lỗi
Gộp mọi cách xử lý lỗi ở trên vào một chỗ, đây là bộ giải sẵn sàng chạy production để bạn mang đi dùng luôn. Lớp wrapper này tự lo:
- gửi task kèm
invisible=1và pollingres.phpcho tới khi có kết quả; - phân biệt lỗi cấu hình (không thử lại) với lỗi tạm thời (thử lại có backoff);
- trả về
Nonekhi gặpERROR_CAPTCHA_UNSOLVABLEđể bạn xin task mới.
Dùng trực tiếp và quay lại các phần trên khi cần hiểu vì sao mỗi nhánh xử lý lại cần thiết:
import requests
import time
import logging
logger = logging.getLogger(__name__)
class InvisibleRecaptchaSolver:
def __init__(self, api_key, max_retries=3):
self.api_key = api_key
self.max_retries = max_retries
self.base_url = "https://ocr.captchaai.com"
def solve(self, sitekey, page_url, proxy=None):
"""Solve invisible reCAPTCHA with automatic retry on transient errors."""
for attempt in range(1, self.max_retries + 1):
try:
token = self._attempt_solve(sitekey, page_url, proxy)
if token:
return token
except Exception as e:
logger.warning(f"Attempt {attempt} failed: {e}")
if attempt < self.max_retries:
time.sleep(2 ** attempt)
raise Exception(f"Failed to solve after {self.max_retries} attempts")
def _attempt_solve(self, sitekey, page_url, proxy):
params = {
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1
}
if proxy:
params["proxy"] = proxy
params["proxytype"] = "HTTP"
# Submit task
resp = requests.get(f"{self.base_url}/in.php", params=params)
if "ERROR" in resp.text:
error = resp.text.strip()
if error in ("ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"):
raise Exception(f"Configuration error (do not retry): {error}")
if error == "ERROR_ZERO_BALANCE":
raise Exception("Account balance is zero — add funds")
raise Exception(f"Submit error: {error}")
if not resp.text.startswith("OK|"):
raise Exception(f"Unexpected submit response: {resp.text}")
task_id = resp.text.split("|")[1]
# Poll for result
for _ in range(60):
time.sleep(5)
result = requests.get(f"{self.base_url}/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id
})
if result.text.startswith("OK|"):
return result.text.split("|")[1]
if result.text == "CAPCHA_NOT_READY":
continue
if result.text == "ERROR_CAPTCHA_UNSOLVABLE":
logger.warning("Captcha unsolvable — will retry with new task")
return None
raise Exception(f"Poll error: {result.text}")
raise Exception("Solve timed out after 5 minutes")
# Usage
solver = InvisibleRecaptchaSolver("YOUR_API_KEY")
token = solver.solve(
sitekey="6LdKlZEU...",
page_url="https://staging.example.com/qa-login"
)
print(f"Token: {token[:50]}...")
const axios = require("axios");
class InvisibleRecaptchaSolver {
constructor(apiKey, maxRetries = 3) {
this.apiKey = apiKey;
this.maxRetries = maxRetries;
this.baseUrl = "https://ocr.captchaai.com";
}
async solve(sitekey, pageUrl, proxy) {
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const token = await this._attemptSolve(sitekey, pageUrl, proxy);
if (token) return token;
} catch (err) {
console.warn(`Attempt ${attempt} failed: ${err.message}`);
if (attempt < this.maxRetries) {
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
}
}
}
throw new Error(`Failed to solve after ${this.maxRetries} attempts`);
}
async _attemptSolve(sitekey, pageUrl, proxy) {
const params = {
key: this.apiKey,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageUrl,
invisible: 1,
};
if (proxy) {
params.proxy = proxy;
params.proxytype = "HTTP";
}
// Submit task
const submitResp = await axios.get(`${this.baseUrl}/in.php`, { params });
if (submitResp.data.includes("ERROR")) {
const error = submitResp.data.trim();
if (["ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"].includes(error)) {
throw new Error(`Configuration error (do not retry): ${error}`);
}
throw new Error(`Submit error: ${error}`);
}
const taskId = submitResp.data.split("|")[1];
// Poll for result
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get(`${this.baseUrl}/res.php`, {
params: { key: this.apiKey, action: "get", id: taskId },
});
if (result.data.startsWith("OK|")) {
return result.data.split("|")[1];
}
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data === "ERROR_CAPTCHA_UNSOLVABLE") return null;
throw new Error(`Poll error: ${result.data}`);
}
throw new Error("Solve timed out after 5 minutes");
}
}
// Usage
const solver = new InvisibleRecaptchaSolver("YOUR_API_KEY");
solver.solve("6LdKlZEU...", "https://staging.example.com/qa-login").then((token) => {
console.log(`Token: ${token.substring(0, 50)}...`);
});
Đọc tiếp
- reCAPTCHA vô hình hoạt động thế nào và cách giải — nền tảng về cơ chế vô hình
- Cách giải reCAPTCHA v2 bằng API — quy trình giải v2 tiêu chuẩn để đối chiếu
- Lỗi reCAPTCHA v2 thường gặp và cách khắc phục — các mẫu lỗi của v2 tiêu chuẩn
- Mã lỗi CaptchaAI: tham khảo đầy đủ — bảng tra toàn bộ mã lỗi
Bài viết liên quan
- Giải reCAPTCHA vô hình bằng Python
- Lỗi CAPTCHA ảnh dạng lưới thường gặp và cách sửa
- So sánh reCAPTCHA v2 và bản vô hình