使用 Ruby 求解 reCAPTCHA Enterprise

作者:solvethree阅读需 1 分钟

使用 Ruby 的 Net::HTTP 创建 reCAPTCHA Enterprise 任务,并轮询至就绪或失败。以下示例直接调用 solvethree 的 REST API,无需 Ruby SDK。

本页内容

配置站点和环境

  1. 1.

    启用站点配置文件

    在 solvethree 控制台中设置目标主机名,并确保其配置在你的工作区中处于启用状态。发送主机名和操作不会创建配置。请参阅站点配置设置。

  2. 2.

    设置环境变量

    将 SOLVETHREE_API_KEY 设置为工作区 API 密钥,将 SOLVETHREE_PROXY 设置为 HTTP 或 HTTPS 代理 URL;向目标提交令牌时也要使用此代理。请将密钥保存在服务器端。

  3. 3.

    修改示例输入

    将 accounts.example.com 替换为目标主机名,而不是完整 URL,并将 login 替换为该目标要求的操作。请求使用一个 proxy,不要再添加 proxyCandidates。不要包含 type 和 url。省略 recaptchaType 时会选择默认值 enterprise-v3。

将示例保存为 solve.rb,设置上述变量后使用 Ruby 运行。它会加载 net/http、json 和 securerandom;JSON 用于处理请求正文和 API 响应。

创建一次,然后按任务 ID 轮询

Ruby · 创建一次并进行有限次轮询
require 'net/http'
require 'json'
require 'securerandom'

api = 'https://api.solvethree.com/v1'
input = {site: 'accounts.example.com', action: 'login', proxy: ENV.fetch('SOLVETHREE_PROXY'), requestId: SecureRandom.uuid}
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 180
call = lambda do |method, path, body = nil|
  remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
  raise 'Local deadline reached' if remaining <= 0
  uri = URI(api + path)
  request = method == :post ? Net::HTTP::Post.new(uri) : Net::HTTP::Get.new(uri)
  request['Authorization'] = 'Bearer ' + ENV.fetch('SOLVETHREE_API_KEY')
  request['Accept'] = 'application/json'
  if body
    request['Content-Type'] = 'application/json'
    request.body = JSON.generate(body)
  end
  # Net::HTTP's own proxy configuration is separate from input[:proxy].
  http = Net::HTTP.new(uri.hostname, uri.port, nil)
  http.use_ssl = uri.scheme == 'https'
  http.open_timeout = [10, remaining].min
  http.read_timeout = [25, remaining].min
  http.write_timeout = [25, remaining].min
  http.max_retries = 0
  response = http.request(request)
  raise "API HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
  JSON.parse(response.body)
end

task_id = nil
begin
  result = call.call(:post, '/tasks?waitMs=0', input)
  task_id = result.fetch('id')
  while %w[pending solving].include?(result.fetch('status'))
    result = call.call(:get, '/tasks/' + URI.encode_www_form_component(task_id) + '?waitMs=15000')
    sleep(1) if %w[pending solving].include?(result.fetch('status'))
  end
  raise "Task failed: #{result['errorCode']}" unless result['status'] == 'ready'
  puts "Ready task: #{task_id}"
  # Submit result['solution']['token'] with its userAgent/secChUa and original proxy.
rescue StandardError => error
  warn "Recovery requestId=#{input[:requestId]} taskId=#{task_id || 'unknown'}"
  raise error
end

脚本发送一次创建请求,保存返回的任务 ID,并在任务状态为 pending 或 solving 时持续获取该任务。只有 ready 才提供可用的求解结果。每个响应都必须具有成功的 HTTP 状态,才能将其正文解析为成功响应数据。

HTTPS 已显式启用。自动重试已禁用;重定向会被 HTTP 状态检查拒绝,而不会被跟随。任务正文中的代理用于求解,不会配置 Ruby 到 solvethree API 的连接。

共享的单调时钟截止时间为轮询提供 180 秒的本地时间预算。连接、读取和写入超时均以每次请求开始时的剩余时间为上限。应将该预算视为轮询限制,而不是对正在进行的请求强制取消的计时器。Ruby Net::HTTP 文档介绍了各项超时设置。

使用令牌对应的浏览器身份信息提交令牌

示例打印的是已就绪任务的 ID,而不是令牌。从就绪结果中读取 solution.token,并将其放入目标请求所要求的令牌字段。不要改为发送任务 ID。

将返回的 solution.userAgent 用于 User-Agent 请求头,将 solution.secChUa 用于 Sec-CH-UA。通过提供给此任务的同一代理,向预期目标提交。不要用无关浏览器的请求头替换返回的身份信息。

任务就绪表示 solvethree 已生成求解结果。请单独检查目标的响应,以确定它是否接受了你的应用请求。

处理失败,避免创建重复任务

脚本抛出 API HTTP 错误。

检查非成功 HTTP 响应。这是请求级错误,而不是任务的终态结果。

试试这样做: 重试前先诊断该响应。不要反复提交未经修改的无效输入。

任务进入 failed 状态。

检查结果中的 errorCode 和 error。示例抛出的消息仅包含 errorCode。

试试这样做: 停止轮询该任务,并处理报告的原因。求解失败会退款;失败不一定意味着余额不足。

在收到任务 ID 前创建请求超时。

保留写入标准错误输出的恢复用 requestId。超时并不能证明创建失败。

试试这样做: 使用相同的请求 ID 提交完全相同的任务请求来恢复。不要直接重新运行脚本:重新运行会生成新的 ID,可能创建另一个任务。

轮询达到本地截止时间或连接中断。

保留日志中记录的任务 ID 和请求 ID。

试试这样做: 创建另一个任务前,先获取已知任务 ID 对应的任务。本地异常并不代表任务已进入终态。

要支持跨进程重启恢复,请在发送 POST 前保留请求 ID 和原始任务输入,并在任务 ID 返回后将其保存。使用相同输入复用请求 ID 会返回现有任务;保留该 ID 但更改输入会导致冲突。有关下一步诊断,请参阅 API 错误处理。

运行您的第一个任务。

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

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