عندما يفشل حل اختبار CAPTCHA بعد كل عمليات إعادة المحاولة، يتم فقدان بيانات المهمة إلا إذا قمت بالتقاطها. تقوم قائمة انتظار الأحرف الميتة (DLQ) بتخزين المهام الفاشلة لإعادة المحاولة أو التحليل أو التنبيه لاحقًا - لذلك لا يتم إسقاط أي عمل بصمت.
عندما تفشل المهام
الأسباب الشائعة التي تؤدي إلى انتهاء مهمة CAPTCHA في DLQ:
ERROR_CAPTCHA_UNSOLVABLE- لم يتمكن القائم بالحل من إكمال التحديERROR_NO_SLOT_AVAILABLE- جميع العمال مشغولون، ويتم إعادة المحاولة مرهقة- المهلة – لم يُرجع Solver نتيجة خلال الموعد النهائي
- أخطاء في الشبكة – انقطع الاتصال أثناء الاستقصاء
وبدون DLQ، تنتج حالات الفشل هذه سطر سجل ويتم نسيانها.
Python: DLQ في الذاكرة مع إعادة المحاولة
import time
import json
import requests
from collections import deque
from dataclasses import dataclass, asdict
from typing import Optional
API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
@dataclass
class FailedTask:
sitekey: str
page_url: str
error: str
attempts: int
timestamp: float
task_id: Optional[str] = None
class DeadLetterQueue:
def __init__(self, max_size=1000, max_retries=3):
self._queue = deque(maxlen=max_size)
self.max_retries = max_retries
def push(self, task: FailedTask):
self._queue.append(task)
print(f"[dlq] Added: {task.error} (attempts: {task.attempts})")
def pop(self) -> Optional[FailedTask]:
return self._queue.popleft() if self._queue else None
def size(self) -> int:
return len(self._queue)
def peek_all(self) -> list:
return [asdict(t) for t in self._queue]
def export_json(self, path: str):
with open(path, "w") as f:
json.dump(self.peek_all(), f, indent=2)
print(f"[dlq] Exported {self.size()} tasks to {path}")
dlq = DeadLetterQueue(max_retries=3)
def solve_captcha(sitekey, page_url, max_retries=3):
for attempt in range(max_retries + 1):
try:
resp = requests.post(SUBMIT_URL, data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1",
}, timeout=15)
data = resp.json()
if data["status"] != 1:
raise Exception(data["request"])
task_id = data["request"]
for _ in range(24):
time.sleep(5)
poll = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
}, timeout=15).json()
if poll["status"] == 1:
return poll["request"]
if poll["request"] != "CAPCHA_NOT_READY":
raise Exception(poll["request"])
raise TimeoutError(f"Task {task_id} timed out")
except Exception as e:
if attempt == max_retries:
dlq.push(FailedTask(
sitekey=sitekey,
page_url=page_url,
error=str(e),
attempts=attempt + 1,
timestamp=time.time(),
))
return None
time.sleep(2 ** attempt)
return None
# Process a batch
urls = [f"https://example.com/page/{i}" for i in range(5)]
for url in urls:
token = solve_captcha("6Le-SITEKEY", url)
if token:
print(f"Solved: {token[:40]}...")
print(f"\nDLQ size: {dlq.size()}")
الناتج المتوقع:
Solved: 03AGdBq26ZfPxL...
Solved: 03AGdBq27AbCdE...
[dlq] Added: ERROR_CAPTCHA_UNSOLVABLE (attempts: 4)
Solved: 03AGdBq28FgHiJ...
[dlq] Added: Task 71823460 timed out (attempts: 4)
DLQ size: 2
إعادة المحاولة من DLQ
def retry_dlq(dlq: DeadLetterQueue, max_retries=2):
retried = 0
recovered = 0
while dlq.size() > 0:
task = dlq.pop()
if task.attempts >= dlq.max_retries + max_retries:
print(f"[dlq] Permanently failed: {task.sitekey} — {task.error}")
continue
retried += 1
token = solve_captcha(
task.sitekey, task.page_url, max_retries=max_retries
)
if token:
recovered += 1
print(f"[dlq-retry] Recovered: {token[:40]}...")
print(f"[dlq] Retried: {retried}, Recovered: {recovered}")
# Run DLQ retry after main batch
retry_dlq(dlq)
JavaScript: DLQ مع ثبات الملف
const fs = require('fs');
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
const DLQ_FILE = './captcha-dlq.json';
class DeadLetterQueue {
constructor(maxRetries = 3) {
this.maxRetries = maxRetries;
this.queue = this._load();
}
push(task) {
this.queue.push({
...task,
timestamp: Date.now(),
});
this._save();
console.log(`[dlq] Added: ${task.error} (attempts: ${task.attempts})`);
}
pop() {
const task = this.queue.shift();
if (task) this._save();
return task || null;
}
size() {
return this.queue.length;
}
_load() {
try {
return JSON.parse(fs.readFileSync(DLQ_FILE, 'utf8'));
} catch {
return [];
}
}
_save() {
fs.writeFileSync(DLQ_FILE, JSON.stringify(this.queue, null, 2));
}
}
const dlq = new DeadLetterQueue(3);
async function solveCaptcha(sitekey, pageurl, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: { key: API_KEY, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: 1 }
});
if (submit.data.status !== 1) throw new Error(submit.data.request);
const taskId = submit.data.request;
for (let i = 0; i < 24; i++) {
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) return poll.data.request;
if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
}
throw new Error(`Task ${taskId} timed out`);
} catch (err) {
if (attempt === maxRetries) {
dlq.push({ sitekey, pageurl, error: err.message, attempts: attempt + 1 });
return null;
}
await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
}
}
}
// Process tasks
(async () => {
for (let i = 0; i < 5; i++) {
const token = await solveCaptcha('6Le-SITEKEY', `https://example.com/page/${i}`);
if (token) console.log(`Solved: ${token.substring(0, 40)}...`);
}
console.log(`DLQ size: ${dlq.size()}`);
})();
تحليل DLQ
تصدير وتحليل المهام الفاشلة للعثور على الأنماط:
# Export DLQ for analysis
dlq.export_json("failed-tasks.json")
# Analyze error distribution
from collections import Counter
errors = Counter(t["error"] for t in dlq.peek_all())
for error, count in errors.most_common():
print(f" {error}: {count}")
استخدم هذه البيانات من أجل:
- حدد مفاتيح الموقع التي تفشل باستمرار - وتحقق مما إذا كانت المعلمات صحيحة
- ترتبط المهلات الموضعية خلال ساعات محددة - مع تحميل واجهة برمجة التطبيقات
- ابحث عن أخطاء الشبكة - تحقق من صحة الوكيل
استكشاف الأخطاء وإصلاحها
| مشكلة | السبب | إصلاح |
|---|---|---|
| DLQ ينمو إلى أجل غير مسمى | لا تتم معالجة المحاولات | جدولة تصريف DLQ الدوري باستخدام retry_dlq() |
| تمت إعادة محاولة نفس المهمة إلى الأبد | لا يوجد حد أقصى للمحاولة | تحقق من task.attempts قبل إعادة الانتظار |
| ملف DLQ تالف | يكتب المتزامنة | استخدم قفل الملف أو قم بالتبديل إلى Redis/database |
| المهام المفقودة عند التعطل | DLQ في الذاكرة فقط | استخدم DLQ المستند إلى الملف أو المدعوم من Redis |
الأسئلة الشائعة
هل يجب أن أستخدم DLQ داخل الذاكرة أم مستمرًا؟
استخدم الذاكرة الداخلية للبرامج النصية قصيرة المدى. استخدم الملفات المستندة إلى الملفات أو المدعومة من Redis للخدمات طويلة الأمد حيث قد تؤدي إعادة تشغيل العملية إلى فقدان المهام الموضوعة في قائمة الانتظار.
متى يجب أن أتجاهل مهمة بشكل دائم؟
بعد 2-3 عمليات إعادة محاولة DLQ (فوق عمليات إعادة المحاولة الأصلية). إذا فشلت المهمة أكثر من 6 مرات، فمن المحتمل أن تكون المعلمات خاطئة - قم بتسجيلها والمضي قدمًا.
هل يمكنني دمج هذا مع نمط قاطع الدائرة؟
نعم. يمنع قاطع الدائرة إرسال الطلبات أثناء انقطاع التيار، ويلتقط DLQ أي مهام تفشل قبل رحلات الدائرة. يرىنمط قواطع الدائرة.
لا تفقد أبدًا مهمة CAPTCHA الفاشلة مرة أخرى مع CaptchaAI
احصل على مفتاح API الخاص بك علىcaptchaai.com.