캡차 솔버 오류 처리 - 재시도, 백오프, 절대 재시도하면 안 되는 것
캡차 풀이는 통제할 수 없는 시스템에 대한 네트워크 호출이며, 예고 없이 바뀔 수 있는 챌린지를 대상으로 합니다. 실패를 얼마나 잘 처리하느냐에 따라, 파이프라인이 우아하게 성능을 낮출지 재시도 폭주로 시간을 낭비할지가 갈립니다.
Scarnox에서는 경제성이 도움을 줍니다. 실패한 풀이는 무료이므로 재시도는 시간만 소모하고 다른 비용은 없습니다. 하지만 잘못된 실패를 재시도하면 그 시간도 낭비되며, 지나치게 재시도하면 그 자체로 문제를 일으킬 수 있습니다. 이 가이드는 Python 작업에 그대로 붙여 넣을 수 있는 재시도 정책을 제공합니다.
Scarnox는 현재 PopularCaptcha를 풉니다. 같은 오류 처리는 어떤 캡차 솔버에도 적용되므로, 아래 정책은 캡차 유형에 의존하지 않습니다.
항상 errorId부터 확인하세요
풀이 응답에는 errorId 필드가 있습니다. 0은 성공이며 토큰은 solution.token에 있습니다. 다른 값은 풀이가 실패했다는 뜻입니다. 실패 응답에는 errorCode와 errorDescription도 함께 옵니다. 둘 다 기록하세요. 코드는 나중에 검색할 때 쓰고, 설명은 실행이 잘못되었을 때 읽게 됩니다.
{"errorId": 1, "errorCode": "...", "errorDescription": "..."}
HTTP 상태가 200이라고 해서 성공이라고 가정하지 마세요. 매번 errorId를 확인하세요.
재시도할 실패
일부 실패는 일시적입니다. 이런 경우에는 지수 백오프와 짧은 무작위 지연을 두고 재시도하세요.
- 네트워크 오류: 연결 재설정, DNS 문제, 내 쪽에서 발생한 읽기 타임아웃.
- 서버 오류: HTTP 5xx 응답. 서비스 쪽 문제를 뜻합니다.
- 요청 제한: HTTP 429. 요청을 너무 빨리 보내고 있다는 뜻이니 속도를 낮추세요.
- 타임아웃: 풀이가 클라이언트의 대기 시간보다 오래 걸렸습니다. 아직 설정하지 않았다면 더 긴 타임아웃으로 재시도하세요.
다음 시도에서 내 쪽 변경 없이도 성공할 수 있으므로, 이런 실패는 재시도할 가치가 있습니다.
재시도 전에 고쳐야 할 실패
다른 실패는 기다린다고 사라지지 않습니다. 같은 입력으로 재시도하면 같은 방식으로 실패합니다. 먼저 이것들을 고치세요.
- 잘못된 작업 유형.
captcha_type의 철자가 문서와 정확히 같은지 확인하세요. - 잘못되었거나 없는 sitekey. 페이지에서 sitekey를 다시 읽으세요. 오래된 배포에서 가져온 sitekey가 흔한 원인입니다.
- 잘못된 페이지 URL.
site_url은 위젯을 보여 주는 페이지여야 합니다. - 프록시 문제. 죽었거나, 오타가 있거나, 차단된 프록시는 모든 시도를 실패하게 합니다. 프록시를 먼저 단독으로 테스트하세요. 프록시 대 프록시 없음을 참고하세요.
- 계정 문제. 잘못된 키나 필요한 잔액이 없는 계정은, 누군가 계정을 고치기 전까지 모든 시도를 실패하게 합니다. 사람에게 알리세요. 루프를 돌며 재시도하지 마세요.
두 목록 어디에도 없는 실패는 먼저 고쳐야 할 실패로 취급하세요. 오류 텍스트를 보고 판단한 뒤, 맞는 목록에 코드를 추가하세요. 목록은 짧고 검토된 상태로 유지하세요.
그대로 붙여 넣을 수 있는 정책
import os
import random
import time
import requests
API_URL = "https://api.scarnox.com/api/tasks/create"
HEADERS = {"Authorization": f"Bearer {os.environ['SCARNOX_KEY']}"}
MAX_ATTEMPTS = 4
class SolveError(Exception):
def __init__(self, code: str, description: str, retryable: bool):
super().__init__(f"{code}: {description}")
self.code = code
self.retryable = retryable
def solve_once(payload: dict) -> str:
try:
r = requests.post(API_URL, headers=HEADERS, json=payload, timeout=185)
except requests.RequestException as exc:
# 내 쪽의 네트워크 오류나 타임아웃: 재시도해도 안전합니다.
raise SolveError("NETWORK", str(exc), retryable=True)
if r.status_code == 429 or r.status_code >= 500:
raise SolveError(f"HTTP_{r.status_code}", r.text[:200], retryable=True)
try:
data = r.json()
except ValueError:
raise SolveError(f"HTTP_{r.status_code}", r.text[:200], retryable=False)
if data.get("errorId") == 0:
return data["solution"]["token"]
# API가 실패를 보고했습니다. 일시적이라고 확인된 코드만 재시도하세요.
code = data.get("errorCode", "UNKNOWN")
retryable = code in TRANSIENT_CODES
raise SolveError(code, data.get("errorDescription", ""), retryable=retryable)
# 로그에서 일시적이라고 확인한 후에만 API 오류 코드를 여기에 추가하세요.
TRANSIENT_CODES: set[str] = set()
def solve(payload: dict) -> str:
for attempt in range(MAX_ATTEMPTS):
try:
return solve_once(payload)
except SolveError as err:
last_attempt = attempt == MAX_ATTEMPTS - 1
if not err.retryable or last_attempt:
raise
delay = min(30, 2 ** attempt) + random.random() # 백오프 + 지터
print(f"attempt {attempt + 1} failed ({err.code}), retrying in {delay:.1f}s")
time.sleep(delay)
raise RuntimeError("unreachable")
이 정책은 먼저 고쳐야 할 실패를 절대 재시도하지 않으며, 고정된 횟수 이후에는 멈춥니다. TRANSIENT_CODES 집합은 의도적으로 비어 있게 시작합니다. 로그에서 일시적이라는 것이 확인된 뒤에만 코드를 추가하세요.
지터가 중요한 이유
워커 50개가 같은 순간에 실패하고 모두 정확히 2초씩 잔다면, 함께 재시도하고 함께 다시 실패합니다. 몇 분의 1초 정도의 무작위 지연이 이들을 흩어 줍니다. 대형 API 제공업체들도 자기 클라이언트에 대해 대체로 같은 조언을 합니다.
타임아웃
HTTP 타임아웃은 일반적인 풀이 시간보다 훨씬 길게, 약 185초로 설정하세요. 요청은 토큰이 준비될 때까지 블록되므로, 짧은 클라이언트 타임아웃은 느리지만 성공할 풀이를 실패로 바꾸고, 그 실패를 쓸모없이 재시도하게 만듭니다. 풀이보다 긴 클라이언트 타임아웃이 안전한 설정입니다.
토큰은 캐시할 수 없습니다
토큰은 약 2분 안에 만료됩니다. 실행 사이에 토큰을 캐시하거나, 두 번째 제출에 같은 토큰을 재사용하지 마세요. 토큰을 사용한 뒤 제출이 실패했다면, 오래된 토큰을 다시 제출하지 말고 새로 풀이하세요. 자세한 타이밍은 토큰 만료 가이드에서 다룹니다.
여러 워커 간의 동시성과 백오프
많은 워커가 하나의 키를 공유한다면, 한 워커의 백오프가 다른 워커를 늦추지는 않습니다. 세마포어나 큐처럼 동시 풀이 수에 공유 제한을 두세요. 재시도 폭주가 요청 폭주로 바뀌지 않게 합니다. 단순한 동시성 상한만으로도 요청 제한 실패의 대부분을 시작 전에 막을 수 있습니다.
도움이 되는 로깅
실패한 시도마다 errorCode, 시도 번호, sitekey, 페이지 호스트, 재시도 여부를 기록하세요. API 키나 프록시 비밀번호는 기록하지 마세요. 시간당 코드별 횟수를 세는 대시보드는, 문제가 내 코드에 있는지, 프록시에 있는지, 서비스에 있는지를 빠르게 알려 줍니다.
다음 단계
무료 체험으로 오류 처리를 테스트해 보세요. 계정을 만들고 무료 체험 크레딧으로 시작하세요. 카드 등록은 필요 없습니다.