GuzzleでCAPTCHAタスクAPIを使用する

著者:solvethree読了時間:1分

Guzzle 7でタスクを作成し、readyまたはfailedになるまでそのIDをポーリングします。問題が発生した場合に備えてrequestIdを保持してください。接続タイムアウトは、タスク作成が失敗した証拠にはなりません。

これはREST連携であり、公式PHP SDKではありません。

このページの内容

reCAPTCHAリクエストを準備する

  • ComposerでGuzzle 7をインストールします。インストールコマンドはサンプルの冒頭にあります。
  • SOLVETHREE_API_KEYにワークスペースのAPIキーを、SOLVETHREE_PROXYにプロキシURLを設定します。キーはサーバー側で保持してください。
  • 解決を要求する前に、コンソールで対象サイトの有効なプロファイルを作成してください。サイトプロファイルの設定を参照してください。
  • accounts.example.comとloginを対象のホスト名とアクションに置き換えてください。siteフィールドには完全なURLではなくホスト名を指定します。

このサンプルは、recaptchaTypeのデフォルトであるreCAPTCHA Enterprise v3を要求します。typeフィールドやurlフィールドを追加しないでください。proxyを1つ指定します。代わりにproxyCandidatesを使用する場合は、proxyを完全に省略してください。

一度だけ作成し、固定の制限時間内でポーリングする

Guzzle · 1回のPOSTと上限付きポーリング
<?php
// composer require guzzlehttp/guzzle:^7
require __DIR__.'/vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://api.solvethree.com/v1/',
    'headers' => ['Authorization' => 'Bearer '.getenv('SOLVETHREE_API_KEY'), 'Accept' => 'application/json'],
    'allow_redirects' => false,
    'http_errors' => false,
]);
$input = ['site' => 'accounts.example.com', 'action' => 'login',
    'proxy' => getenv('SOLVETHREE_PROXY'), 'requestId' => bin2hex(random_bytes(16))];
$deadline = microtime(true) + 180;
$call = function (string $method, string $path, array $options = []) use ($client, $deadline): array {
    $remaining = $deadline - microtime(true);
    if ($remaining <= 0) throw new RuntimeException('Local deadline reached');
    $response = $client->request($method, $path, $options + ['timeout' => min(25, $remaining), 'connect_timeout' => min(10, $remaining)]);
    if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
        throw new RuntimeException('API HTTP '.$response->getStatusCode());
    }
    return json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR);
};
$id = null;
try {
    $result = $call('POST', 'tasks?waitMs=0', ['json' => $input]);
    $id = $result['id'];
    while (in_array($result['status'], ['pending', 'solving'], true)) {
        $result = $call('GET', 'tasks/'.rawurlencode($id).'?waitMs=15000');
        if (in_array($result['status'], ['pending', 'solving'], true)) usleep(1000000);
    }
    if ($result['status'] !== 'ready') {
        throw new RuntimeException('Task failed: '.($result['errorCode'] ?? 'unknown'));
    }
    echo 'Ready task: '.$id.PHP_EOL;
    // Submit $result['solution']['token'] with userAgent/secChUa and the same proxy.
} catch (Throwable $error) {
    fwrite(STDERR, 'Recovery requestId='.$input['requestId'].' taskId='.($id ?? 'unknown').PHP_EOL);
    throw $error;
}

クライアントはBearerキーで認証します。base_uriの末尾は/v1/で、リクエストパスは相対パスのため、このAPIプレフィックスが維持されます。Guzzleのjsonオプションはタスク入力をJSONとして送信します。

ヘルパーは作成用POSTを一度だけ実行します。その後、statusがpendingまたはsolvingの間、返されたタスクIDをポーリングします。作成とポーリングは180秒の制限時間を共有します。各HTTP呼び出しは残り時間を上限とし、リクエストタイムアウトは最大25秒、接続タイムアウトは最大10秒です。新しいポーリングを開始しても、全体の制限時間はリセットされません。

リダイレクトは無効です。http_errorsをfalseにすると、ヘルパーは成功時のペイロードをデコードする前に、2xx以外のレスポンスを明示的に確認できます。接続エラーやタイムアウトが抑制されるわけではありません。

  • 2xx以外のHTTPレスポンスでは、API HTTPエラーがスローされます。解決処理の失敗として扱うのではなく、レスポンスのステータスと本文を調べて原因を特定してください。
  • タスクが最終状態のfailedになると、ポーリングは停止します。サンプルはそのerrorCodeを例外としてスローします。診断メッセージについては、タスクのerrorフィールドも確認してください。失敗した解決処理は返金されます。
  • 使用可能な解決結果が含まれるのはreadyだけです。出力される「Ready task」の行は、タスクの準備完了を示すもので、対象ウェブサイトで受け入れられたことを示すものではありません。

別のタスクに料金を支払わずにタイムアウトしたリクエストを復旧する

POSTを送信する前に、生成したrequestIdと正確なタスク入力を保持してください。catchブロックはrequestIdと、作成時に返された場合はtaskIdを出力します。この出力は復旧に役立ちますが、送信前にリクエストを保存することの代わりにはなりません。

  1. 1.

    タスクIDがある場合は、そのタスクを取得する

    GET /v1/tasks/:idでステータスを確認してください。ヘルパーのローカルな期限に達しても、リモートのタスクが失敗したとは限りません。

  2. 2.

    IDが返されずに作成がタイムアウトした場合は、requestIdで復旧する

    保存したrequestIdで同じタスクリクエストを送信してください。入力が一致すれば既存のタスクが返されます。同じIDで異なる入力を送ると競合します。

  3. 3.

    復旧を手早く済ませようとしてスクリプトを再実行しない

    実行のたびに新しいrequestIdが生成されます。別のIDを生成するPOSTの自動再試行を追加したり、無効な入力を変更せずに無条件で再試行したりしないでください。

同じプロキシ経由でトークンを送信する

このサンプルは対象への接続前に終了します。タスクIDではなくsolution.tokenを送信し、返されたsolution.userAgentとsolution.secChUaを対象リクエストのUser-AgentヘッダーとSec-CH-UAヘッダーに使用してください。

タスクのJSON内のproxyは、解決処理に使用するプロキシをsolvethreeに指定するものです。GuzzleからAPIへの接続や、その後の対象へのリクエストを設定するものではありません。対象へのリクエストは、同じプロキシを使用するよう別途設定してください。

APIクライアントのデフォルトのAuthorizationヘッダーがウェブサイトに送信されないよう、対象リクエストには別のクライアントを使用してください。トークン、返されたブラウザー識別情報、プロキシ、意図した送信先をひとまとまりで管理します。その後のリクエストが一致しない場合、準備済みのトークンでも拒否される可能性があります。

最初のタスクを実行しましょう。

$5のワークスペースクレジットで始められます。

ワークスペースを作成
GuzzleでCAPTCHAタスクAPIを使用する · solvethree