Quando uma solução CAPTCHA falha após todas as novas tentativas, os dados da tarefa são perdidos, a menos que você os capture. Uma fila de mensagens mortas (DLQ) armazena tarefas com falha para nova tentativa, análise ou alerta posterior, para que nenhum trabalho seja descartado silenciosamente.
Quando as tarefas falham
Razões comuns pelas quais uma tarefa CAPTCHA termina no DLQ:
ERROR_CAPTCHA_UNSOLVABLE— O solucionador não conseguiu completar o desafioERROR_NO_SLOT_AVAILABLE— Todos os trabalhadores ocupados, novas tentativas esgotadas- Tempo limite — o Solver não retornou um resultado dentro do prazo
- Erros de rede — A conexão caiu durante a pesquisa
Sem um DLQ, essas falhas produzem uma linha de log e são esquecidas.
Python: DLQ na memória com nova tentativa
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()}")
Resultado esperado:
Solved: 03AGdBq26ZfPxL...
Solved: 03AGdBq27AbCdE...
[dlq] Added: ERROR_CAPTCHA_UNSOLVABLE (attempts: 4)
Solved: 03AGdBq28FgHiJ...
[dlq] Added: Task 71823460 timed out (attempts: 4)
DLQ size: 2
Tentando novamente do 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 com persistência de arquivo
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()}`);
})();
Análise DLQ
Exporte e analise tarefas com falha para encontrar padrões:
# 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}")
Use esses dados para:
- Identifique as chaves do site que falham consistentemente → verifique se os parâmetros estão corretos
- Tempos limite pontuais durante horários específicos → correlacionados com a carga da API
- Encontre erros de rede → verifique a integridade do proxy
Solução de problemas
| Problema | Causa | Correção |
|---|---|---|
| DLQ cresce indefinidamente | Não processando novas tentativas | Agende drenagem DLQ periódica com retry_dlq() |
| A mesma tarefa repetida para sempre | Sem limite máximo de tentativas | Verifique task.attempts antes de colocar novamente na fila |
| Arquivo DLQ corrompido | Gravações simultâneas | Use o bloqueio de arquivo ou mude para Redis/database |
| Tarefas perdidas em caso de falha | Somente DLQ na memória | Use DLQ baseado em arquivo ou com suporte de Redis |
Perguntas frequentes
Devo usar um DLQ na memória ou persistente?
Use na memória para scripts de curta duração. Use baseado em arquivo ou com suporte de Redis para serviços de longa execução em que a reinicialização de um processo perderia tarefas na fila.
Quando devo descartar permanentemente uma tarefa?
Após 2-3 tentativas de DLQ (além das tentativas originais). Se uma tarefa falhar mais de 6 vezes no total, os parâmetros provavelmente estão errados – registre-a e siga em frente.
Posso combinar isso com o padrão do disjuntor?
Sim. O disjuntor impede o envio de solicitações durante uma interrupção e o DLQ captura quaisquer tarefas que falhem antes do circuito desarmar. VerPadrão de disjuntor.
Nunca mais perca uma tarefa CAPTCHA com falha com CaptchaAI
Obtenha sua chave API emcaptchaai.com.