Resuelve tareas de CAPTCHA de forma concurrente en Go
Usa un único cliente compartido del SDK de Go y un grupo fijo de workers. Asigna a cada trabajo su propio contexto y envía cada solución en cuanto esté lista, en lugar de acumular tokens para un lote posterior.
En esta página
Prepara el cliente y el destino
- Usa Go 1.22 o posterior y el módulo oficial
solvethree.com/sdken la versiónv0.1.0. Los detalles de instalación están en la documentación de integración con Go. - Configura
SOLVETHREE_API_KEYySOLVETHREE_PROXYen el entorno del servidor. No incluyas la clave de API en el código del navegador. - Sustituye
accounts.example.comyloginpor el nombre de host y la acción de destino. El espacio de trabajo necesita un perfil activo para ese sitio; enviar el nombre de host no crea uno. Consulta la configuración del perfil del sitio.
El ejemplo solicita reCAPTCHA Enterprise, la opción predeterminada. Proporciona un proxy y deja intencionadamente Type y URL sin definir: las solicitudes de reCAPTCHA usan el nombre de host del sitio y la acción, no esos campos.
Limita el trabajo, no el número de clientes
El cliente admite el uso concurrente de forma segura. Este ejemplo inicia cuatro workers y les distribuye ocho trabajos mediante un canal sin búfer. El productor espera cuando todos los workers están ocupados, en lugar de lanzar una goroutine por cada trabajo. Cuatro es un valor ilustrativo de configuración de la aplicación, no un límite del servicio 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 crea un contexto independiente de 180 segundos para cada trabajo. Solve crea la tarea una sola vez y espera su resultado. Como el ejemplo omite RequestID, el SDK genera uno para esa creación. Las llamadas fallidas se registran y continue hace que solo ese worker pase al siguiente trabajo; no detiene el lote.
Mantén la cancelación dentro del bucle, como se muestra, para que cada trabajo libere los recursos de su contexto sin demora. La cancelación detiene la espera local; no cancela el trabajo remoto.
Procesa la solución antes de tomar otro trabajo
El ejemplo imprime el ID de la tarea lista; no envía nada al destino. Sustituye el comentario por tu solicitud al destino y luego informa del resultado del envío de ese trabajo antes de que el worker tome otro. No añadas tokens a un slice para usarlos después de workers.Wait.
- Envía
result.Solution.Token, noresult.ID. - Usa
SuccessfulProxycon la tarea original y el resultado listo para identificar el proxy que funcionó. Realiza el envío a través de ese proxy con la identidad del navegador devuelta enUserAgentySecChUA. - Mantén el envío dentro del contexto del trabajo, antes de
cancel(). Así, el plazo de 180 segundos abarca tanto la resolución como el envío; usa un contexto de envío independiente si necesitas un margen de tiempo separado.
Informa por separado de la resolución y del envío al destino. Una tarea lista proporciona una solución utilizable, pero solo la respuesta del destino indica si aceptó la solicitud de la aplicación.
Recupera un trabajo interrumpido sin reiniciar el lote
Registrar un error basta para este pequeño ejemplo, pero una integración que permita la recuperación debe conservar los datos de entrada exactos de la tarea de cada trabajo y los ID de solicitud y tarea disponibles. Un tiempo de espera agotado no demuestra que la creación de la tarea haya fallado.
- Extrae
*solvethree.Errorpara obtenerRequestIDyTaskID. Si conoces el ID de la tarea, reanuda la consulta conGetTaskusando un contexto nuevo. - Si no sabes si la creación se completó y tienes el ID de solicitud, vuelve a enviar los datos de entrada originales con ese mismo
RequestID. La misma solicitud devuelve la tarea existente; cambiar los datos de entrada y reutilizar el ID provoca un conflicto. - Para
*TaskFailedError, registraErrorCodey recupera la tarea fallida si necesitas su texto deError. Deja de consultar esa tarea y corrige la causa, en lugar de volver a enviar a ciegas los mismos datos de entrada no válidos.
Distingue los errores HTTP de los fallos terminales de las tareas. errors.As puede extraer un *APIError anidado con StatusCode y RetryAfter. Solve ya reintenta las respuestas temporales de consulta con HTTP 429, 502, 503 o 504, respetando Retry-After dentro del plazo del contexto. La creación de tareas nunca se reintenta automáticamente.
Fuentes y referencia de la API
Ejecuta tu primera tarea.
Empieza con $5 de crédito en tu espacio de trabajo.