C#のSeleniumでCAPTCHA APIを使用する
HttpClient で solvethree を呼び出し、タスクが ready になるまで待ってから、アプリケーションの CAPTCHA フィールドに solution.token を設定します。Chrome はターゲットページを開く前に、同じプロキシと返されたブラウザー識別情報を使用する必要があります。
以下の例では、.NET 8 と Selenium Chrome を使用して、自分が管理するフォームを送信します。これは REST を直接使用する実装であり、公式の C# SDK や Selenium プラグインではありません。
このページの内容
プロジェクトとターゲットを準備する
- 既定の暗黙的な using を有効にした .NET 8 コンソールプロジェクトを使用してください。NuGet パッケージ
Selenium.WebDriverとSelenium.Supportを、どちらもバージョン4.52.0で追加してください。 - Chrome をローカルにインストールしてください。Selenium Manager が互換性のある Chrome ドライバーを提供します。
- ターゲットのホスト名に対して、ワークスペースで利用できる有効なサイトプロファイルがあることを確認してください。ホスト名とアクションを送信しても、サイトプロファイルは作成されません。先にサイトプロファイルの設定手順に従ってください。
- URL に認証情報を含まない、IP 許可リストに登録されたプロキシを使用してください。この例では、同じプロキシを solvethree と Chrome に渡します。
| 変数 | 指定する値 |
|---|---|
SOLVETHREE_API_KEY | ワークスペースの API キー。ページ内ではなく、自動化を実行する信頼できるマシン上で保持してください。 |
SOLVETHREE_PROXY | 解決とブラウザーからの送信の両方に使用する、IP 許可リストに登録されたプロキシの URL。 |
TARGET_URL | アプリケーションのフォームページの完全な URL。この例では、そのホスト名を抽出してタスクの site に使用します。 |
別のアクションを使用する実装では、例の action を login から変更してください。この例では、既定の recaptchaType である enterprise-v3 を使用します。タスクリクエストに type フィールドや url フィールドは不要です。
タスクを作成してフォームを送信する
API ヘルパーはワークスペースキーで認証し、成功以外の HTTP レスポンスを、成功ペイロードとして解析する前に拒否します。プログラムはタスクを 1 つ作成し、ステータスが pending または solving の間はその ID をポーリングし、タスクが ready になってから Chrome を起動します。
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;
var key = Environment.GetEnvironmentVariable("SOLVETHREE_API_KEY")
?? throw new Exception("Set SOLVETHREE_API_KEY");
var proxy = Environment.GetEnvironmentVariable("SOLVETHREE_PROXY")
?? throw new Exception("Set SOLVETHREE_PROXY");
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key);
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(180));
async Task<JsonObject> Api(HttpMethod method, string path, JsonObject? body = null)
{
using var request = new HttpRequestMessage(method, "https://api.solvethree.com/v1" + path);
if (body is not null)
request.Content = new StringContent(body.ToJsonString(), Encoding.UTF8, "application/json");
using var response = await http.SendAsync(request, deadline.Token);
response.EnsureSuccessStatusCode();
return JsonNode.Parse(await response.Content.ReadAsStringAsync(deadline.Token))!.AsObject();
}
var requestId = Guid.NewGuid().ToString(); // Save with these inputs for recovery.
var input = new JsonObject {
["site"] = new Uri(Environment.GetEnvironmentVariable("TARGET_URL") ?? throw new Exception("Set TARGET_URL")).Host, ["action"] = "login",
["proxy"] = proxy, ["requestId"] = requestId
};
var task = await Api(HttpMethod.Post, "/tasks?waitMs=0", input);
while (task["status"]!.GetValue<string>() is "pending" or "solving")
{
var id = Uri.EscapeDataString(task["id"]!.GetValue<string>());
task = await Api(HttpMethod.Get, "/tasks/" + id + "?waitMs=15000");
if (task["status"]!.GetValue<string>() is "pending" or "solving")
await Task.Delay(1000, deadline.Token);
}
if (task["status"]!.GetValue<string>() != "ready")
throw new Exception("Task failed: " + task["errorCode"]?.GetValue<string>()
+ ": " + task["error"]?.GetValue<string>());
var solution = task["solution"]!.AsObject();
// Use solution["token"], ["userAgent"] and ["secChUa"] with the original proxy.
Console.WriteLine("Ready task: " + task["id"]!.GetValue<string>());
var target = Environment.GetEnvironmentVariable("TARGET_URL") ?? throw new Exception("Set TARGET_URL");
if (!string.IsNullOrEmpty(new Uri(proxy).UserInfo)) throw new Exception("Use an IP-allowlisted proxy");
var options = new ChromeOptions();
options.AddArgument("--proxy-server=" + proxy);
options.AddArgument("--user-agent=" + solution["userAgent"]!.GetValue<string>());
using var browser = new ChromeDriver(options);
browser.ExecuteCdpCommand("Network.enable", new Dictionary<string, object>());
browser.ExecuteCdpCommand("Network.setExtraHTTPHeaders", new Dictionary<string, object> {
["headers"] = new Dictionary<string, object> { ["sec-ch-ua"] = solution["secChUa"]!.GetValue<string>() }
});
browser.Navigate().GoToUrl(target);
var wait = new WebDriverWait(browser, TimeSpan.FromSeconds(20));
var field = wait.Until(driver => driver.FindElement(By.Name("g-recaptcha-response")));
browser.ExecuteScript("arguments[0].value=arguments[1]; arguments[0].dispatchEvent(new Event('input',{bubbles:true}));",
field, solution["token"]!.GetValue<string>());
browser.FindElement(By.CssSelector("button[type=submit]")).Click();
wait.Until(driver => driver.FindElement(By.Id("success"))); // Your application's success marker.HTTP タイムアウトの 30 秒、API の期限の 180 秒、Selenium の待機時間の 20 秒は、この例の設定であり、サービスの処理時間を保証するものではありません。アプリケーションのタイムアウトポリシーに合わせて調整してください。
アプリケーションに合わせて受け渡しを調整する
- 1.
プロキシと返された識別情報を組み合わせて使用する
ChromeOptions は元のプロキシと
solution.userAgentを適用します。CDP コマンドはネットワーク機能を有効にし、ページ遷移の前にsolution.secChUaからsec-ch-uaを設定します。この順序を守ってください。先にページを開き、後から識別情報を変更しないでください。 - 2.
アプリケーションが読み取る場所にトークンを書き込む
この例では、
g-recaptcha-responseという名前のフィールドを見つけ、solution.tokenを代入し、input イベントをディスパッチします。アプリケーションが別の場所からトークンを読み取る場合は、この受け渡し処理を置き換えてください。フィールドへの入力が役立つのは、アプリケーションの送信リクエストにその値が実際に含まれる場合だけです。 - 3.
送信と成功確認のセレクターを置き換える
このサンプルは
button[type=submit]をクリックし、ID がsuccessの要素を待ちます。実際のアプリケーションのセレクターを使用し、単にボタンがクリックされたことではなく、送信した操作が成功したことを確認できる成功マーカーを選んでください。
タスクが ready であることは、解が利用可能になったことを意味します。送信後の確認は別です。アプリケーションのリクエストを受け入れるかどうかは、引き続きターゲットが判断します。
不要なタスクを作成せずに失敗に対処する
- API ヘルパーが HTTP レスポンスに対して例外をスローする。
HTTP ステータスとエラーレスポンスを調べてください。これは、タスクが
failedになることとは異なります。試してみましょう: 再試行する前に、報告されたリクエストまたは認証の問題を修正してください。無効な入力を変更せずに繰り返し送信しないでください。
- タスクが
failedになる。 errorCodeとerrorを確認してください。タスクエラーの例としてsite-unreachableがあります。試してみましょう: そのタスクのポーリングを停止し、報告された原因に対処してください。解決に失敗した場合は返金されます。
- タスクは ready だが、ターゲットが送信を拒否する。
まず、アプリケーションの送信リクエストを調べてください。タスク ID や古いトークンではなく、
solution.tokenが含まれていますか。その後、プロキシ、ユーザーエージェント、クライアントヒント、ターゲット URL を確認してください。試してみましょう: 追加の解決に料金を支払う前に、トークンの受け渡しや識別情報の不一致を修正してください。
- 送信ボタンのクリック後に Selenium がタイムアウトする。
ページとアプリケーションのレスポンスを調べてください。
successセレクターはこの例で用意したものであり、汎用的な受け入れ確認の指標ではありません。試してみましょう: アプリケーションの実際の成功条件に置き換えるか、アプリケーションが返した拒否に対処してください。
このサンプルは requestId を生成しますが、自動では永続化しません。タイムアウトからの復旧が必要な場合は、作成前にタスクの正確な入力とともに保存してください。作成がタイムアウトした場合は、同じ入力を同じ ID で再送信し、既存のタスクを復旧してください。レスポンスが失われたという理由だけで新しい ID を生成しないでください。異なる入力で ID を再利用すると競合します。すでにタスク ID がある場合は、代わりにそのタスクを取得してください。
参照元とAPIリファレンス
最初のタスクを実行しましょう。
$5のワークスペースクレジットで始められます。