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は、その対象が要求するアクションに置き換えます。リクエストでは1つのproxyを使用します。proxyCandidatesも追加しないでください。typeとurlは含めないでください。recaptchaTypeを省略すると、デフォルトのenterprise-v3が選択されます。

例をsolve.rbとして保存し、これらの変数を設定してからRubyで実行してください。net/http、json、securerandomが読み込まれ、JSONがリクエストボディとAPIレスポンスを処理します。

1回だけ作成し、タスクIDをポーリングする

Ruby · 1回の作成と有限回のポーリング
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

このスクリプトは作成リクエストを1回送信し、返されたタスク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