将 Go SDK 与 chromedp 配合使用
先求解,再使用相同的代理和返回的浏览器身份启动 Chrome。将令牌插入应用表单,提交表单,然后等待应用结果。任务就绪表示你已获得解决方案,并不表示网站已接受它。
本页内容
设置模块、配置和代理
使用 go get 安装 solvethree.com/[email protected] 和 github.com/chromedp/[email protected]。SDK 要求 Go 1.22+,但此 chromedp 版本要求 Go 1.25+,因此组合示例应使用后者。它的类型化 API 与较早的 chromedp 示例不同。
运行程序前,在工作区中为目标主机名创建一个已启用的站点配置。发送主机名和操作并不会创建配置。
TARGET_URL:包含表单的完整 HTTPS 页面 URL。任务使用其主机名作为Site;Chrome 则访问完整 URL。SOLVETHREE_PROXY:单个 HTTP 或 HTTPS 代理 URL。此示例要求代理通过 IP 白名单授权,且 URL 中不含凭据。SOLVETHREE_API_KEY:工作区 API 密钥。将其保留在 Go 进程中,不要放入页面 JavaScript。
此示例请求 login 操作,并使用默认的 Enterprise v3 reCAPTCHA 模式。请将操作改为应用所期望的值。
求解、启动 Chrome 并提交
程序等待 SDK 求解完成后,才创建 Chrome 的分配器。它使用相同的单个代理和 result.Solution.UserAgent 配置 Chrome,然后在导航前将返回的 SecChUA 设置为 sec-ch-ua 请求头。不要替换为无关的浏览器身份,也不要在求解与提交之间切换代理。
// 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
}此示例特意只使用一个代理,因此无需确定选中了哪个候选代理。如果之后使用 ProxyCandidates,请通过 SuccessfulProxy(originalTask, readyResult) 获取成功使用的代理,并让 Chrome 使用该代理。
替换演示表单的假设条件
插入令牌时,会先使用 json.Marshal,再构造 JavaScript 表达式。请保留此转义步骤:它将令牌作为 JavaScript 字符串值传递,而不是将未经转义的文本插入可执行代码。
- 将
[name="g-recaptcha-response"]替换为应用实际提交的字段。示例会设置该字段的值并派发 input 事件;如果应用需要其他提交方式,请相应调整。 - 将
button[type="submit"]替换为预期的提交控件。点击前,请准备好所有必需的表单值、CSRF 字段和会话状态。 - 将
#success替换为真正表示应用已接受提交的结果。点击完成或令牌字段已填充,都不代表这一结果。
SDK 不会向目标提交令牌。如果目标拒绝请求,先检查发出的表单:确认其中包含 result.Solution.Token,而不是任务 ID 或旧的字段值,并检查代理和浏览器身份是否仍然一致。
处理截止时间,避免重复求解
求解上下文的截止时间为 180 秒。求解完成后,Chrome 使用单独的 60 秒截止时间。run 函数会返回错误,以便在 main 记录错误之前,先执行延迟的上下文取消和浏览器清理。
取消求解上下文只会停止等待,不会停止远程工作。SDK 仅创建一次任务,只重试临时的轮询失败,从不重试创建任务的 POST 请求。因此,超时并不意味着应该立即提交新任务。
- 发生取消或到达截止时间时,使用
errors.Is识别上下文错误。使用errors.As提取*solvethree.Error,并保留其TaskID和RequestID。 - 如果有任务 ID,使用
GetTask获取任务。否则,使用相同的请求 ID 重放完全相同的原始任务输入来恢复。不携带该 ID 再次调用会创建新的逻辑请求;复用该 ID 却更改输入会产生冲突。 - 对于
*TaskFailedError,检查ErrorCode,并获取失败任务以查看其错误文本。该任务已进入终态:停止轮询并修正原因,不要使用未更改的输入重试。 - 对于 HTTP API 错误,提取嵌套的
*APIError,以检查StatusCode和RetryAfter。不要将 HTTP 失败与已进入终态的任务失败混淆。
来源与 API 参考
运行您的第一个任务。
使用 $5 工作区额度开始体验。