C#でcaptcha解決:HttpClientを使った.NETの例(2026年)
C#では、captchaソルバーはHttpClientをラップした小さなクラスです。Scarnox APIにサイトキーとページURLをPOSTし、JSONレスポンスからトークンを読み取り、サイトのフォームに送信します。このガイドでは、.NET 8向けに、Scarnoxが現在プロキシなしで解決しているPopularCaptchaのクラスを作ります。
無料のトライアルクレジットで始める(カード登録は不要)と、まず自分が管理しているページでコードを試せます。他のものに使う前に、ぜひそうしてください。
必要なもの
- .NET 8 SDK(
dotnet --versionで確認)。 - ScarnoxのAPIキー。環境変数から読み込みます。
- ページのPopularCaptchaウィジェットのサイトキー。見つけ方はcaptchaのサイトキーの見つけ方を参照してください。
プロジェクトを作る
dotnet new console -n CaptchaDemo
cd CaptchaDemo
追加のパッケージは不要です。System.Net.HttpとSystem.Text.Jsonはフレームワークに含まれています。
ソルバークラス
以下のクラスは、Bearerヘッダーと185秒のタイムアウトを持つHttpClientを1つ保持します。トークンを返すか、APIのエラーメッセージを付けて例外を投げます。
// ScarnoxSolver.cs
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
public sealed class ScarnoxSolver : IDisposable
{
private const string ApiUrl = "https://api.scarnox.com/api/tasks/create";
private readonly HttpClient _http;
public ScarnoxSolver(string apiKey)
{
_http = new HttpClient { Timeout = TimeSpan.FromSeconds(185) };
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
}
public async Task<string> SolveAsync(
string sitekey,
string pageUrl,
bool invisible = false,
CancellationToken ct = default)
{
var body = new Dictionary<string, object>
{
["captcha_type"] = "PopularCaptchaTaskProxyless",
["site_key"] = sitekey,
["site_url"] = pageUrl,
};
if (invisible)
{
body["is_invisible"] = true;
}
// トークンの準備ができると戻る。ポーリングは不要。
using var resp = await _http.PostAsJsonAsync(ApiUrl, body, ct);
var data = await resp.Content.ReadFromJsonAsync<CreateTaskResponse>(cancellationToken: ct)
?? throw new InvalidOperationException(
$"HTTP {(int)resp.StatusCode}: empty response");
if (data.ErrorId != 0)
{
throw new InvalidOperationException($"{data.ErrorCode}: {data.ErrorDescription}");
}
return data.Solution?.Token
?? throw new InvalidOperationException("Response had no token");
}
public void Dispose() => _http.Dispose();
}
public sealed class CreateTaskResponse
{
[JsonPropertyName("errorId")]
public int ErrorId { get; set; }
[JsonPropertyName("errorCode")]
public string? ErrorCode { get; set; }
[JsonPropertyName("errorDescription")]
public string? ErrorDescription { get; set; }
[JsonPropertyName("solution")]
public Solution? Solution { get; set; }
}
public sealed class Solution
{
[JsonPropertyName("token")]
public string? Token { get; set; }
}
errorIdのチェックが最も重要な行です。APIは成功時に0を返します。それ以外の値の場合はerrorCodeとerrorDescriptionが付くので、このクラスはその両方を表に出します。
トークンを送信する
トークンはサイトのフォームに送ります。フォームを読み込んだセッションをPOSTでも再利用するため、Cookieコンテナを持つ2つ目のHttpClientを使います。
// Program.cs
using System.Net;
var apiKey = Environment.GetEnvironmentVariable("SCARNOX_API_KEY")
?? throw new InvalidOperationException("Set SCARNOX_API_KEY");
const string pageUrl = "https://example.com/signup";
const string sitekey = "YOUR-SITEKEY-UUID";
using var solver = new ScarnoxSolver(apiKey);
string token = "";
// 一時的な失敗はリトライする。失敗した解決は課金されない。
for (int attempt = 1; attempt <= 3; attempt++)
{
try
{
token = await solver.SolveAsync(sitekey, pageUrl);
break;
}
catch (InvalidOperationException ex) when (attempt < 3)
{
Console.Error.WriteLine($"Attempt {attempt} failed: {ex.Message}");
await Task.Delay(TimeSpan.FromSeconds(attempt));
}
}
var cookies = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cookies, UseCookies = true };
using var site = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(60) };
// 先にGETして、サイトのセッションCookieを設定させる。
await site.GetAsync(pageUrl);
using var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "you@example.com",
["h-captcha-response"] = token,
["g-recaptcha-response"] = token,
});
using var response = await site.PostAsync(pageUrl, form);
Console.WriteLine($"Status: {(int)response.StatusCode}");
実行します。
set SCARNOX_API_KEY=scarnox_YOURAPIKEY
dotnet run
PowerShellでは、代わりに$env:SCARNOX_API_KEY = "scarnox_YOURAPIKEY"を使ってください。
この構成にしている理由
**ソルバーごとにHttpClientを1つ。** HttpClientは呼び出しごとに作るのではなく、再利用するためのものです。ソルバーが1つ持ち、終了時に破棄します。
185秒のタイムアウト。 APIはトークンの準備ができるまでブロックします。短いタイムアウトにすると、もうすぐ完了する解決をキャンセルしてしまいます。
型付きのデシリアライズ。 ReadFromJsonAsyncがJSONをCreateTaskResponseにマッピングするので、エラーとトークンのチェックが普通のC#のコードのように読めます。
フォーム送信。 サイトはh-captcha-responseとg-recaptcha-responseの両方を受け取ります。サイトによってどちらかを読むためです。Cookieコンテナは、GETとPOSTの間でセッションを維持します。
タイミング
トークンは約2分で期限切れになります。送信の直前に解決してください。バッチの中で早めに解決して、送信がずっと後になると、トークンは古くなっています。captchaトークンの期限切れで詳しく説明しています。
不可視のウィジェット
ウィジェットが不可視の場合は、SolveAsyncにinvisible: trueを渡してください。リクエストにはis_invisible: trueが含まれます。ページによっては、サーバー側のクライアントではトリガーできないJavaScriptのコールバックが必要です。その場合は、ブラウザの自動化ツールのほうが適しています。パターンは不可視captchaのガイドで扱っています。
責任あるスクレイピング
プログラムが公開ページをスクレイピングするなら、まずrobots.txtとサイトの利用規約を確認してください。リクエストの間には遅延を入れ、必要な個人データだけを保持してください。法的な背景はWebスクレイピングは合法かを参照してください。
料金
Scarnoxは成功した解決ごとに課金します。失敗した解決は無料です。料金の詳細は料金ページを参照してください。無料のトライアルクレジットで始める(カード登録は不要)こともできます。