Khi giải CAPTCHA không thành công vào lúc 3 giờ sáng, nhật ký văn bản đơn giản như "Error solving captcha" sẽ không giúp ích được gì. Nhật ký JSON có cấu trúc với ID tác vụ, loại hình ảnh xác thực, thời gian giải quyết và mã lỗi cho phép bạn lọc, tìm kiếm và cảnh báo chính xác những gì đã xảy ra.
Tại sao ghi nhật ký có cấu trúc
| Văn bản thuần túy | JSON có cấu trúc |
|---|---|
Captcha solved in 12.3s |
{"event":"captcha_solved","task_id":"abc123","type":"recaptcha_v2","solve_time_ms":12300} |
| Khó phân tích | Máy có thể đọc được |
| Tìm kiếm chỉ theo Grep | Lọc theo trường bất kỳ |
| Không có mối tương quan | Liên kết ID tác vụ gửi → thăm dò → tiêm |
Python: nhật ký cấu trúc
import structlog
import time
structlog.configure(
processors=[
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.add_log_level,
structlog.processors.JSONRenderer(),
],
logger_factory=structlog.PrintLoggerFactory(),
)
log = structlog.get_logger()
Ghi nhật ký vòng đời giải quyết
import requests
API_KEY = "YOUR_API_KEY"
def solve_captcha(captcha_type, sitekey, page_url, proxy=None):
solve_log = log.bind(
captcha_type=captcha_type,
site_url=page_url,
sitekey=sitekey[:12] + "...",
)
# Submit
start = time.time()
solve_log.info("captcha_submit_start")
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1",
}).json()
if resp["status"] != 1:
solve_log.error("captcha_submit_failed", error=resp["request"])
return None
task_id = resp["request"]
submit_ms = int((time.time() - start) * 1000)
solve_log = solve_log.bind(task_id=task_id)
solve_log.info("captcha_submitted", submit_ms=submit_ms)
# Poll
for attempt in range(24):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": "1"
}).json()
if result["status"] == 1:
solve_ms = int((time.time() - start) * 1000)
solve_log.info(
"captcha_solved",
solve_time_ms=solve_ms,
poll_attempts=attempt + 1,
token_length=len(result["request"]),
)
return result["request"]
if result["request"] != "CAPCHA_NOT_READY":
solve_log.error(
"captcha_solve_failed",
error=result["request"],
poll_attempts=attempt + 1,
)
return None
solve_log.warning("captcha_solve_timeout", poll_attempts=24)
return None
Đầu ra:
{"event":"captcha_submit_start","captcha_type":"recaptcha_v2","site_url":"https://example.com","sitekey":"6Le-wvkSAAAA...","timestamp":"2025-07-15T10:30:00Z","level":"info"}
{"event":"captcha_submitted","task_id":"71845302","submit_ms":245,"timestamp":"2025-07-15T10:30:00Z","level":"info"}
{"event":"captcha_solved","task_id":"71845302","solve_time_ms":18230,"poll_attempts":4,"token_length":580,"timestamp":"2025-07-15T10:30:18Z","level":"info"}
Node.js: pino
const pino = require('pino');
const log = pino({
level: 'info',
timestamp: pino.stdTimeFunctions.isoTime,
});
Ghi nhật ký vòng đời giải quyết
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
async function solveCaptcha(captchaType, sitekey, pageUrl) {
const taskLog = log.child({
captchaType,
siteUrl: pageUrl,
sitekey: sitekey.substring(0, 12) + '...',
});
const start = Date.now();
taskLog.info('captcha_submit_start');
const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: API_KEY, method: 'userrecaptcha',
googlekey: sitekey, pageurl: pageUrl, json: 1,
},
});
if (submit.data.status !== 1) {
taskLog.error({ error: submit.data.request }, 'captcha_submit_failed');
return null;
}
const taskId = submit.data.request;
const boundLog = taskLog.child({ taskId });
boundLog.info({ submitMs: Date.now() - start }, 'captcha_submitted');
for (let attempt = 1; attempt <= 24; attempt++) {
await new Promise(r => setTimeout(r, 5000));
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
});
if (poll.data.status === 1) {
boundLog.info({
solveTimeMs: Date.now() - start,
pollAttempts: attempt,
tokenLength: poll.data.request.length,
}, 'captcha_solved');
return poll.data.request;
}
if (poll.data.request !== 'CAPCHA_NOT_READY') {
boundLog.error({ error: poll.data.request, pollAttempts: attempt }, 'captcha_solve_failed');
return null;
}
}
boundLog.warn({ pollAttempts: 24 }, 'captcha_solve_timeout');
return null;
}
Tham chiếu trường nhật ký
| trường | loại | Mô tả |
|---|---|---|
event |
chuỗi | Tên sự kiện: captcha_submitted, captcha_solved, v.v. |
task_id |
chuỗi | ID nhiệm vụ CaptchaAI cho mối tương quan |
captcha_type |
chuỗi | recaptcha_v2, turnstile, image, v.v. |
site_url |
chuỗi | URL trang mục tiêu |
solve_time_ms |
số nguyên | Tổng thời gian từ khi gửi đến khi giải quyết |
poll_attempts |
số nguyên | Số lượng yêu cầu thăm dò ý kiến được thực hiện |
error |
chuỗi | Mã lỗi từ CaptchaAI |
token_length |
số nguyên | Độ dài của mã thông báo được trả về |
Lọc và cảnh báo
Tìm tất cả các thất bại trong giờ qua
# With jq
cat captcha.log | jq 'select(.level == "error" and .event == "captcha_solve_failed")'
Cảnh báo tỷ lệ thất bại cao
# Count errors vs successes in a rolling window
from collections import deque
class ErrorRateMonitor:
def __init__(self, window_size=100, threshold=0.2):
self.results = deque(maxlen=window_size)
self.threshold = threshold
def record(self, success):
self.results.append(success)
if len(self.results) >= 50:
error_rate = 1 - sum(self.results) / len(self.results)
if error_rate > self.threshold:
log.warning(
"captcha_error_rate_high",
error_rate=round(error_rate, 3),
window=len(self.results),
)
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Nhật ký quá dài dòng | Ghi lại mọi nỗ lực thăm dò ý kiến | Chỉ ghi nhật ký các sự kiện gửi, giải quyết và thất bại |
| Không thể liên kết các sự kiện | Thiếu ID nhiệm vụ | Liên kết sớm task_id với log.bind() hoặc log.child() |
| Nhật ký không thể tìm kiếm được | Định dạng văn bản thuần túy | Chuyển sang JSON bằng structlog hoặc pino |
| Dữ liệu nhạy cảm trong nhật ký | Ghi nhật ký khóa API đầy đủ | Không bao giờ ghi lại khóa API; cắt bớt khóa trang web |
Câu hỏi thường gặp
Tôi có nên đăng nhập mã thông báo CAPTCHA không?
Ghi lại độ dài mã thông báo, không phải mã thông báo đầy đủ. Mã thông báo có thể có hơn 500 ký tự và thêm khối lượng nhật ký không cần thiết.
Cấp độ nhật ký nào cho CAPCHA_NOT_READY?
Tuyệt đối không ghi lại thông tin đó - điều này được mong đợi trong quá trình bỏ phiếu. Chỉ ghi lại kết quả cuối cùng.
Xây dựng quy trình làm việc CAPTCHA có thể quan sát được bằng CaptchaAI
Nhận khóa API của bạn tạicaptchaai.com.