Go에서 CAPTCHA 작업 동시 해결하기

작성: solvethree읽는 데 3분

하나의 공유 Go SDK 클라이언트와 고정된 워커 풀을 사용하세요. 각 작업에 별도의 컨텍스트를 부여하고, 토큰을 모아 나중에 일괄 제출하지 말고 솔루션이 준비되는 즉시 제출하세요.

이 페이지의 내용

클라이언트와 대상 준비

  • Go 1.22 이상과 공식 solvethree.com/sdk 모듈 버전 v0.1.0을 사용하세요. 설치 방법은 Go 연동 문서를 참고하세요.
  • 서버 환경에 SOLVETHREE_API_KEY와 SOLVETHREE_PROXY를 설정하세요. API 키를 브라우저 코드에 포함하지 마세요.
  • accounts.example.com과 login을 대상 호스트 이름과 액션으로 바꾸세요. 워크스페이스에 해당 사이트의 활성 프로필이 있어야 합니다. 호스트 이름을 제출해도 프로필이 생성되지는 않습니다. 사이트 프로필 설정을 참고하세요.

이 예제는 기본값인 reCAPTCHA Enterprise를 요청합니다. 프록시 하나를 제공하고 Type과 URL은 의도적으로 설정하지 않습니다. reCAPTCHA 요청은 이 필드들이 아니라 사이트 호스트 이름과 액션을 사용합니다.

클라이언트 수가 아닌 동시 작업 수 제한

이 클라이언트는 동시에 사용해도 안전합니다. 이 예제는 워커 4개를 시작하고 버퍼 없는 채널을 통해 작업 8개를 전달합니다. 생산자는 작업마다 고루틴을 실행하는 대신 모든 워커가 사용 중이면 대기합니다. 워커 4개는 애플리케이션 설정의 예시일 뿐, solvethree 서비스의 제한이 아닙니다.

Go · 워커 4개와 작업별 즉시 처리
package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "sync"
    "time"
    solvethree "solvethree.com/sdk"
)

func main() {
    client, err := solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"), nil)
    if err != nil { log.Fatal(err) }
    jobs := make(chan int)
    var workers sync.WaitGroup
    for worker := 0; worker < 4; worker++ {
        workers.Add(1)
        go func() {
            defer workers.Done()
            for job := range jobs {
                ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
                input := solvethree.TaskRequest{Site: "accounts.example.com", Action: "login", Proxy: os.Getenv("SOLVETHREE_PROXY")}
                result, err := client.Solve(ctx, input, solvethree.SolveOptions{})
                if err != nil { cancel(); log.Printf("Job %d: %v", job, err); continue }
                fmt.Println("Ready task:", result.ID)
                // Submit this solution now, with its proxy and browser identity.
                cancel()
            }
        }()
    }
    for job := 0; job < 8; job++ { jobs <- job }
    close(jobs)
    workers.Wait()
}

각 워커는 작업마다 별도의 180초 컨텍스트를 생성합니다. Solve는 태스크를 한 번 생성한 후 결과를 기다립니다. 이 예제는 RequestID를 생략하므로 SDK가 생성 요청에 사용할 ID를 생성합니다. 호출이 실패하면 오류를 기록하고, continue는 해당 워커만 다음 작업으로 이동시킵니다. 배치 전체를 중단하지는 않습니다.

예제처럼 루프 안에서 취소하여 각 작업의 컨텍스트 리소스를 신속하게 해제하세요. 취소는 로컬 대기만 중단하며 원격 작업은 취소하지 않습니다.

다음 작업을 받기 전에 솔루션 처리

이 예제는 준비된 태스크 ID를 출력할 뿐, 대상에 아무것도 제출하지 않습니다. 주석을 대상 요청 코드로 바꾸고, 워커가 다음 작업을 받기 전에 해당 작업의 제출 결과를 보고하세요. workers.Wait 이후에 사용하려고 토큰을 슬라이스에 추가하지 마세요.

  • result.ID가 아니라 result.Solution.Token을 제출하세요.
  • 원래 태스크와 준비된 결과를 SuccessfulProxy에 전달하여 성공한 프록시를 확인하세요. 반환된 UserAgent와 SecChUA 브라우저 식별 정보를 사용해 해당 프록시를 통해 제출하세요.
  • cancel() 전에 작업 컨텍스트 안에서 제출하세요. 그러면 180초 제한 시간이 풀이와 제출 모두에 적용됩니다. 별도의 시간 한도가 필요하면 제출용 컨텍스트를 따로 사용하세요.

풀이 결과와 대상 제출 결과를 별도로 보고하세요. 준비된 태스크는 사용 가능한 솔루션을 제공하지만, 애플리케이션 요청이 수락되었는지는 대상의 응답으로만 확인할 수 있습니다.

배치를 다시 시작하지 않고 중단된 작업 복구

이 간단한 예제에서는 오류를 기록하는 것으로 충분하지만, 복구 가능한 연동을 구현하려면 각 작업의 정확한 태스크 입력값과 확보한 요청 ID 및 태스크 ID를 보관해야 합니다. 시간 초과가 발생했다고 해서 태스크 생성이 실패한 것은 아닙니다.

  • *solvethree.Error를 추출하여 RequestID와 TaskID를 얻으세요. 태스크 ID를 알고 있다면 새 컨텍스트에서 GetTask로 조회를 재개하세요.
  • 생성 여부가 불확실하고 요청 ID가 있다면, 동일한 RequestID와 원래 입력값으로 요청을 다시 실행하세요. 동일한 요청은 기존 태스크를 반환합니다. ID를 재사용하면서 입력값을 변경하면 충돌이 발생합니다.
  • *TaskFailedError가 발생하면 ErrorCode를 기록하고, Error 텍스트가 필요하면 실패한 태스크를 조회하세요. 해당 태스크의 폴링을 중단하고, 동일한 잘못된 입력값을 무작정 다시 제출하는 대신 원인을 해결하세요.

HTTP 오류와 태스크의 최종 실패를 구분하세요. errors.As로 StatusCode와 RetryAfter가 포함된 중첩 *APIError를 추출할 수 있습니다. Solve는 HTTP 429, 502, 503 또는 504인 일시적 폴링 응답을 이미 재시도하며, 컨텍스트 제한 시간 내에서 Retry-After를 준수합니다. 태스크 생성은 자동으로 재시도하지 않습니다.

첫 작업을 실행하세요.

워크스페이스 크레딧 $5로 시작하세요.

워크스페이스 만들기
Go에서 CAPTCHA 작업 동시 해결하기 · solvethree