Resolva tarefas de CAPTCHA simultaneamente em Go

Por solvethree4 min de leitura

Use um único cliente compartilhado do SDK Go e um pool fixo de workers. Dê a cada trabalho seu próprio contexto e envie cada solução assim que estiver pronta, em vez de acumular tokens para um lote posterior.

Nesta página

Prepare o cliente e o destino

  • Use Go 1.22 ou mais recente e o módulo oficial solvethree.com/sdk na versão v0.1.0. Os detalhes de instalação estão na documentação de integração com Go.
  • Defina SOLVETHREE_API_KEY e SOLVETHREE_PROXY no ambiente do servidor. Mantenha a chave de API fora do código do navegador.
  • Substitua accounts.example.com e login pelo hostname e pela ação do seu destino. O workspace precisa de um perfil ativo para esse site; enviar o hostname não cria um perfil. Consulte a configuração do perfil do site.

O exemplo solicita reCAPTCHA Enterprise, que é o padrão. Ele fornece um proxy e deixa Type e URL deliberadamente sem definição: as solicitações de reCAPTCHA usam o hostname do site e a ação, não esses campos.

Limite o trabalho, não o número de clientes

O cliente é seguro para uso concorrente. Este exemplo inicia quatro workers e distribui oito trabalhos entre eles por um canal sem buffer. O produtor espera quando todos os workers estão ocupados, em vez de iniciar uma goroutine para cada trabalho. Quatro é uma configuração ilustrativa da aplicação, não um limite do serviço solvethree.

Go · quatro workers e tratamento imediato por trabalho
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()
}

Cada worker cria um contexto separado de 180 segundos para cada trabalho. Solve cria a tarefa uma vez e aguarda o resultado. Como o exemplo omite RequestID, o SDK gera um para essa criação. Uma chamada que falha é registrada no log, e continue faz apenas esse worker avançar para o próximo trabalho; isso não interrompe o lote.

Mantenha o cancelamento dentro do loop, como mostrado, para que cada trabalho libere os recursos do contexto prontamente. O cancelamento interrompe a espera local; ele não cancela o trabalho remoto.

Trate a solução antes de pegar outro trabalho

O exemplo imprime o ID da tarefa pronta; ele não envia nada ao destino. Substitua o comentário pela sua requisição ao destino e informe o resultado do envio desse trabalho antes que o worker pegue outro trabalho. Não acumule tokens em um slice para usá-los após workers.Wait.

  • Envie result.Solution.Token, não result.ID.
  • Use SuccessfulProxy com a tarefa original e o resultado pronto para identificar o proxy bem-sucedido. Faça o envio por esse proxy com a identidade de navegador retornada em UserAgent e SecChUA.
  • Mantenha o envio dentro do contexto do trabalho, antes de cancel(). Assim, o prazo de 180 segundos cobre tanto a resolução quanto o envio; use um contexto de envio separado se precisar de um limite de tempo próprio.

Informe a resolução e o envio ao destino separadamente. Uma tarefa pronta fornece uma solução utilizável, mas só a resposta do destino informa se ele aceitou a requisição da aplicação.

Recupere um trabalho interrompido sem reiniciar o lote

Registrar um erro no log basta para este pequeno exemplo, mas uma integração com suporte à recuperação deve reter os dados de entrada exatos de cada trabalho e os IDs de requisição e tarefa disponíveis. Um timeout não comprova que a criação da tarefa falhou.

  • Extraia *solvethree.Error para obter RequestID e TaskID. Se o ID da tarefa for conhecido, retome a consulta com GetTask em um novo contexto.
  • Se houver incerteza sobre a criação e você tiver o ID da requisição, reenvie os dados de entrada originais com o mesmo RequestID. A mesma requisição retorna a tarefa existente; alterar os dados de entrada reutilizando o ID gera um conflito.
  • Para *TaskFailedError, registre ErrorCode e consulte a tarefa que falhou se precisar do texto de Error. Pare de consultar essa tarefa e resolva a causa, em vez de reenviar cegamente os mesmos dados de entrada inválidos.

Mantenha os erros HTTP separados das falhas terminais de tarefas. errors.As pode extrair um *APIError aninhado com StatusCode e RetryAfter. Solve já repete as consultas que recebem respostas temporárias com HTTP 429, 502, 503 ou 504, respeitando Retry-After dentro do prazo do contexto. A criação de tarefas nunca é repetida automaticamente.

Execute sua primeira tarefa.

Comece com $5 em créditos no espaço de trabalho.

Criar um espaço de trabalho
Resolva tarefas de CAPTCHA simultaneamente em Go · solvethree