Scarnox · Blog

캡차 토큰 만료: 원인과 해결 방법

캡차 토큰은 챌린지를 통과했다는 것을 증명하는 수명이 짧은 증거입니다. Scarnox의 PopularCaptcha 토큰은 발급 후 약 2분 뒤에 만료됩니다. 그 시간이 지난 뒤 토큰을 제출하면, 사이트는 보통 캡차가 만료되었거나 유효하지 않다는 메시지와 함께 폼을 거부합니다. 해결책은 거의 항상 타이밍에 관한 것입니다. 제출 직전에 최대한 가깝게 캡차를 풀고, 각 토큰은 한 번만 사용하고, 첫 시도가 받아들여지지 않았다는 것을 확실히 알 때만 재시도하세요.

토큰이 만료되는 이유

토큰은 영구적인 자격 증명이 아닙니다. 사이트는 사람(또는 그를 대신하는 솔버)이 최근에 챌린지를 완료했는지 알고 싶어 하므로, 토큰에 짧은 수명을 줍니다. 시계는 여러분이 API를 처음 호출한 시점이 아니라 토큰이 발급된 시점부터 시작됩니다. 풀이와 제출 사이에 일어나는 모든 일이 그 2분을 갉아먹습니다.

흔한 원인

해결책: 제출 직전에 풀이하기

가장 단순하고 믿을 만한 패턴은 풀이와 제출을 한 단계로 묶는 것입니다. 토큰을 요청하고, 폼에 넣고, 폼을 POST하며, 그 사이에는 느린 작업을 하지 않습니다.

import requests

API_KEY = "scarnox_YOURAPIKEY"
CREATE_URL = "https://api.scarnox.com/api/tasks/create"

def fresh_token(sitekey: str, page_url: str) -> str:
    r = requests.post(
        CREATE_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "captcha_type": "PopularCaptchaTaskProxyless",
            "site_key": sitekey,
            "site_url": page_url,
        },
        timeout=185,  # 토큰이 준비될 때까지 호출이 블록됩니다
    )
    data = r.json()
    if data.get("errorId") != 0:
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data["solution"]["token"]

def submit_form(page_url: str, sitekey: str, form: dict) -> requests.Response:
    token = fresh_token(sitekey, page_url)  # 게시 직전에 풀이합니다
    return requests.post(
        page_url,
        data={**form, "h-captcha-response": token, "g-recaptcha-response": token},
        timeout=30,
    )

submit_form은 토큰을 캐시나 큐에 보관하지 않는다는 점에 주목하세요. 토큰을 하나 요청하고, 게시하고, 넘어갑니다. 실패한 풀이는 청구되지 않으므로, 실패한 요청도 풀이 쪽에서는 비용이 들지 않습니다.

안전하게 재시도하기

일부 사이트는 만료된 토큰을 분명한 응답으로 거부합니다. 다른 사이트는 오류 메시지가 들어 있는 평범한 페이지를 돌려주므로, 상태 코드만으로는 알 수 없습니다. 만료된 토큰에 대해 사이트가 실제로 돌려주는 응답을 살펴보고, 그에 맞춰 검사를 만드세요. 재시도 루프는 다음과 같을 수 있습니다.

def submit_with_retry(page_url: str, sitekey: str, form: dict, attempts: int = 2):
    for _ in range(attempts):
        resp = submit_form(page_url, sitekey, form)
        if resp.status_code < 400 and "captcha" not in resp.text.lower():
            return resp  # 이 사이트의 응답 기준으로 받아들여졌습니다
    raise RuntimeError("captcha token rejected after retries")

무언가를 만드는 폼에서의 재시도는 조심하세요. 첫 시도가 실제로 받아들여졌는데 코드가 응답을 잘못 읽었다면, 재시도가 중복 계정, 주문, 메시지를 만들 수 있습니다. 첫 게시가 받아들여지지 않았다고 확신할 때만 재시도하고, 검사는 작업 중인 사이트에 맞게 구체적으로 만드세요.

타이밍을 정확하게 유지하기

흔한 실수

문제를 진단할 수 있도록 충분히 기록하기

제출이 실패하면 토큰이 발급된 시간, 폼이 게시된 시간, HTTP 상태, 응답의 처음 몇백 자를 기록하세요. 이 네 가지 값이 있으면 토큰이 오래되어 만료되었는지, 아니면 사이트가 다른 이유로 거부했는지 한눈에 알 수 있습니다. 공유 로그에는 전체 토큰을 남기지 마세요. 유효한 토큰은 그 챌린지 하나에 대한 자격 증명이기 때문입니다.

그래도 실패한다면 확인할 것

  1. 토큰이 반환된 시점과 폼이 게시된 시점 사이의 시간을 출력하세요.
  2. site_url이 캡차를 보여 주는 페이지와 일치하는지 확인하세요.
  3. sitekey를 오래된 복사본이 아니라 현재 페이지에서 읽었는지 확인하세요.
  4. 폼 필드 이름을 확인하세요. PopularCaptcha 토큰은 h-captcha-response에 들어가며, 종종 g-recaptcha-response에도 넣습니다.

모든 오류 응답과 각각의 처리 방법은 캡차 솔버 오류 처리에 있습니다.

다음 단계