Usa el SDK de Go con chromedp
Primero resuelve y después inicia Chrome con el mismo proxy y la identidad de navegador devuelta. Inserta el token en el formulario de la aplicación, envíalo y espera el resultado de la aplicación. Una tarea lista significa que tienes una solución, no que el sitio web la haya aceptado.
En esta página
Configura los módulos, el perfil y el proxy
Instala solvethree.com/[email protected] y github.com/chromedp/[email protected] con go get. El SDK requiere Go 1.22+, pero esta versión de chromedp requiere Go 1.25+, así que usa esa versión para el ejemplo combinado. Su API tipada difiere de la de los ejemplos antiguos de chromedp.
Crea un perfil de sitio activo para el nombre de host de destino en tu espacio de trabajo antes de ejecutar el programa. Enviar un nombre de host y una acción no crea un perfil.
TARGET_URL: la URL HTTPS completa de la página que contiene tu formulario. La tarea usa su nombre de host comoSite; Chrome navega a la URL completa.SOLVETHREE_PROXY: la URL de un único proxy HTTP o HTTPS. Este ejemplo requiere un proxy con lista de IP permitidas y sin credenciales en la URL.SOLVETHREE_API_KEY: la clave de API de tu espacio de trabajo. Mantenla en el proceso de Go, no en el JavaScript de la página.
El ejemplo solicita la acción login y usa el modo predeterminado de reCAPTCHA Enterprise v3. Cambia la acción al valor que espera tu aplicación.
Resuelve, inicia Chrome y envía
El programa espera a que el SDK complete la resolución antes de crear el asignador de Chrome. Configura Chrome con el mismo proxy único y result.Solution.UserAgent, y después establece el SecChUA devuelto como cabecera sec-ch-ua antes de navegar. No sustituyas la identidad del navegador por otra no relacionada ni cambies de proxy entre la resolución y el envío.
// go get github.com/chromedp/[email protected] (requires Go 1.25+)
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/url"
"os"
"time"
"github.com/chromedp/cdproto/network"
"github.com/chromedp/chromedp"
solvethree "solvethree.com/sdk"
)
func main() { if err := run(); err != nil { log.Fatal(err) } }
func run() error {
target,err:=url.Parse(os.Getenv("TARGET_URL"));if err!=nil||target.Scheme!="https"||target.Hostname()=="" { return fmt.Errorf("set an HTTPS TARGET_URL") }
proxy,err:=url.Parse(os.Getenv("SOLVETHREE_PROXY"));if err!=nil||proxy.User!=nil { return fmt.Errorf("use an IP-allowlisted proxy without URL credentials") }
client,err:=solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"),nil);if err!=nil { return err }
ctx,cancel:=context.WithTimeout(context.Background(),180*time.Second);defer cancel()
result,err:=client.Solve(ctx,solvethree.TaskRequest{Site:target.Hostname(),Action:"login",Proxy:proxy.String()},solvethree.SolveOptions{})
if err!=nil { return err }
options:=append(chromedp.DefaultExecAllocatorOptions[:],chromedp.ProxyServer(proxy.String()),chromedp.UserAgent(result.Solution.UserAgent))
allocator,closeAllocator:=chromedp.NewExecAllocator(context.Background(),options...);defer closeAllocator()
browser,closeBrowser:=chromedp.NewContext(allocator);defer closeBrowser()
browser,stop:=context.WithTimeout(browser,60*time.Second);defer stop()
tokenJSON,err:=json.Marshal(result.Solution.Token);if err!=nil { return err }
script:=`(token => {const field=document.querySelector('[name="g-recaptcha-response"]'); field.value=token; field.dispatchEvent(new Event('input',{bubbles:true}));})(`+string(tokenJSON)+`)`
_,err=chromedp.Call(browser,network.SetExtraHTTPHeaders,network.SetExtraHTTPHeadersParams{Headers:network.Headers{"sec-ch-ua":result.Solution.SecChUA}})
if err!=nil { return err }
err=chromedp.Do(browser,
chromedp.Navigate(target.String()),chromedp.WaitReady(chromedp.CSS(`[name="g-recaptcha-response"]`)),
chromedp.Evaluate[chromedp.Void](script),chromedp.Click(chromedp.CSS(`button[type="submit"]`)),
chromedp.WaitVisible(chromedp.ID("success")))
if err!=nil { return err }
// #success is this owned demo form's outcome. Adapt its selectors, token
// parameter and success check to your application, including CSRF/session fields.
return nil
}Este ejemplo usa deliberadamente un solo proxy, por lo que no hay que determinar qué candidato se seleccionó. Si más adelante usas ProxyCandidates, obtén el proxy que tuvo éxito con SuccessfulProxy(originalTask, readyResult) y úsalo para Chrome.
Adapta las suposiciones del formulario de demostración
La inserción del token usa json.Marshal antes de construir la expresión JavaScript. Mantén ese paso de escape: pasa el token como un valor de cadena de JavaScript en lugar de interpolar texto sin escapar en código ejecutable.
- Sustituye
[name="g-recaptcha-response"]por el campo que realmente envía tu aplicación. El ejemplo establece su valor y dispara un evento de entrada; adáptalo si tu aplicación necesita otra vía de envío. - Sustituye
button[type="submit"]por el control de envío previsto. Incluye los valores de formulario, los campos CSRF y el estado de sesión necesarios antes de hacer clic. - Sustituye
#successpor un resultado que realmente indique aceptación en tu aplicación. Que se complete un clic o que el campo del token tenga un valor no constituye ese resultado.
El SDK no envía el token al destino. Si el destino rechaza la solicitud, revisa primero el formulario enviado: confirma que contiene result.Solution.Token, no el ID de la tarea ni un valor anterior del campo, y comprueba que el proxy y la identidad del navegador siguen coincidiendo.
Gestiona los plazos sin crear resoluciones duplicadas
La resolución tiene un plazo de contexto de 180 segundos. Chrome dispone de un plazo independiente de 60 segundos después de la resolución. La función run devuelve errores para que la cancelación diferida del contexto y la limpieza del navegador se ejecuten antes de que main registre un error.
Cancelar el contexto de resolución detiene la espera, no el trabajo remoto. El SDK crea la tarea una sola vez y solo reintenta los fallos temporales de consulta; nunca reintenta el POST de creación. Por tanto, un tiempo de espera agotado no es motivo para enviar una nueva tarea de inmediato.
- En caso de cancelación o vencimiento del plazo, usa
errors.Ispara identificar el error de contexto. Extrae*solvethree.Errorconerrors.Asy conserva sus valoresTaskIDyRequestID. - Si tienes un ID de tarea, recupérala con
GetTask. De lo contrario, recupérala reenviando exactamente los datos de entrada originales de la tarea con el mismo ID de solicitud. Una nueva llamada sin ese ID crea una nueva solicitud lógica; cambiar los datos de entrada al reutilizarlo provoca un conflicto. - Para
*TaskFailedError, revisaErrorCodey recupera la tarea fallida para obtener el texto del error. Esa tarea está en un estado terminal: deja de consultarla y corrige la causa en lugar de reintentar con los mismos datos de entrada. - Si se produce un error HTTP de la API, extrae el
*APIErroranidado para revisarStatusCodeyRetryAfter. No confundas un fallo HTTP con una tarea fallida en estado terminal.
Fuentes y referencia de la API
Ejecuta tu primera tarea.
Empieza con $5 de crédito en tu espacio de trabajo.