Resolva tarefas de CAPTCHA simultaneamente em Go
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/sdkna versãov0.1.0. Os detalhes de instalação estão na documentação de integração com Go. - Defina
SOLVETHREE_API_KEYeSOLVETHREE_PROXYno ambiente do servidor. Mantenha a chave de API fora do código do navegador. - Substitua
accounts.example.comeloginpelo 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.
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ãoresult.ID. - Use
SuccessfulProxycom 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 emUserAgenteSecChUA. - 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.Errorpara obterRequestIDeTaskID. Se o ID da tarefa for conhecido, retome a consulta comGetTaskem 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, registreErrorCodee consulte a tarefa que falhou se precisar do texto deError. 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.
Fontes e referência da API
Execute sua primeira tarefa.
Comece com $5 em créditos no espaço de trabalho.