使用 Go 求解 reCAPTCHA Enterprise

作者:solvethree阅读需 1 分钟

安装 solvethree.com/sdk,并使用目标主机名、操作和代理调用 Solve。SDK 会创建一次任务并轮询结果。随后由你的应用向目标提交令牌。

本页内容

安装并配置 SDK

使用 Go 1.22+。在你的 Go 模块中运行:

安装官方 Go SDK
go get solvethree.com/sdk@v0.1.0
  • 将 SOLVETHREE_API_KEY 设为你的工作区 API 密钥,将 SOLVETHREE_PROXY 设为你的 HTTP(S) 代理 URL。两者都应保存在服务器配置中。
  • 在工作区中为目标主机名创建有效的配置文件。请参阅站点配置文件设置。
  • 将 accounts.example.com 和 login 替换为目标使用的主机名和操作。Site 接受主机名,不含协议或路径。

求解一个任务

将此代码保存为 main.go,然后运行 go run .。

Go · 创建一次并轮询
package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"
    solvethree "solvethree.com/sdk"
)

func main() {
    client, err := solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"), nil)
    if err != nil { log.Fatal(err) }
    ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
    defer cancel()
    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 { log.Fatal(err) }
    fmt.Println("Ready task:", result.ID)
    // Submit result.Solution.Token with its browser identity and original proxy.
}

上下文将本地等待时间限制为 180 秒。Solve 返回就绪的 TaskResult 或错误;无需在外层再加轮询循环。此请求使用默认的 Enterprise v3 模式。如需普通 v3,请设置 RecaptchaType: "v3"。对于 reCAPTCHA,请勿设置 Type 和 URL。

向目标提交令牌

在应用的 CAPTCHA 字段中发送 result.Solution.Token。输出的 result.ID 用于标识任务,并不是令牌。

将 result.Solution.UserAgent 用作 User-Agent,将 result.Solution.SecChUA 用作 sec-ch-ua。使用 solvethree.SuccessfulProxy(input, result) 选出求解成功时使用的代理,并通过该代理发送目标请求。

包含应用的其他表单字段、Cookie 和 CSRF 值,然后检查响应。SDK 会返回解决方案,但不会替你提交应用请求。

等待中断时保留 ID

取消上下文只会停止本地等待,不会停止远程工作。*solvethree.Error 提供 RequestID 和 TaskID。使用 GetTask 获取已知任务;如果无法确定是否已创建,请使用相同的 RequestID 重发完全一致的原始输入。

如需在进程重启后恢复,请在创建任务前分配 RequestID,并将其与任务输入一同保存。*TaskFailedError 表示任务已终止:检查其 ErrorCode,并获取任务以查看错误文本。修正原因后再开始另一次求解。

替代方案:使用标准库调用 REST

如果你希望自行管理 HTTP 调用,此版本直接使用 net/http。请选择此示例或上面的 SDK 示例;同时运行两者会创建两个任务。这里的响应结构体属于此独立示例,因此字段名称与 SDK 类型中的名称不同。

Go 1.22+ · main.go
package main

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "os"
    "time"
)

type Task struct {
    ID string `json:"id"`
    Status string `json:"status"`
    ErrorCode string `json:"errorCode"`
    Error string `json:"error"`
    Solution struct {
        Token string `json:"token"`
        UserAgent string `json:"userAgent"`
        SecChUa string `json:"secChUa"`
    } `json:"solution"`
}

func main() {
    key, proxy := os.Getenv("SOLVETHREE_API_KEY"), os.Getenv("SOLVETHREE_PROXY")
    if key == "" || proxy == "" { panic("Set SOLVETHREE_API_KEY and SOLVETHREE_PROXY") }
    ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
    defer cancel()
    client := &http.Client{Timeout: 30*time.Second}
    api := func(method, path string, body []byte) (Task, error) {
        var task Task
        req, err := http.NewRequestWithContext(ctx, method, "https://api.solvethree.com/v1"+path, bytes.NewReader(body))
        if err != nil { return task, err }
        req.Header.Set("Authorization", "Bearer "+key)
        req.Header.Set("Content-Type", "application/json")
        response, err := client.Do(req)
        if err != nil { return task, err }
        defer response.Body.Close()
        if response.StatusCode < 200 || response.StatusCode >= 300 { return task, fmt.Errorf("API HTTP %d", response.StatusCode) }
        err = json.NewDecoder(response.Body).Decode(&task)
        return task, err
    }
    requestID := fmt.Sprintf("go-%d", time.Now().UnixNano())
    body, err := json.Marshal(map[string]string{
        "site":"accounts.example.com", "action":"login", "proxy":proxy, "requestId":requestID,
    })
    if err != nil { panic(err) }
    // Persist requestID and body before creation if this must survive a restart.
    task, err := api(http.MethodPost, "/tasks?waitMs=0", body)
    if err != nil { panic(fmt.Errorf("request %s: %w", requestID, err)) }
    for task.Status == "pending" || task.Status == "solving" {
        task, err = api(http.MethodGet, "/tasks/"+url.PathEscape(task.ID)+"?waitMs=15000", nil)
        if err != nil { panic(err) }
        if task.Status == "pending" || task.Status == "solving" {
            select { case <-ctx.Done(): panic(ctx.Err()); case <-time.After(time.Second): }
        }
    }
    if task.Status != "ready" { panic("Task failed: "+task.ErrorCode+": "+task.Error) }
    solution := task.Solution
    _ = solution // Apply token and returned browser identity using the same proxy.
    fmt.Println("Ready task:", task.ID)
}

运行您的第一个任务。

使用 $5 工作区额度开始体验。

创建工作区
使用 Go 求解 reCAPTCHA Enterprise · solvethree