サードパーティCAPTCHA認識サービスのC#統合ガイド
Web自動化やテストフレームワークにおいて、手動介入を避けながらCAPTCHA画像を解読する必要がある場面は少なくありません。サードパーティのOCR/CAPTCHA解読プラットフォームのAPIをC#から呼び出すことで、画像送信からテキスト抽出までのフローをプログラム内で完結させられます。本稿では、JSONリクエスト方式とフォームエンコーディッド方式の2つの標準的な通信パターンを実装例を交えて解説します。
JSONベースのAPI連携(例:JSD型プラットフォーム)
多くの現代的なサービスはRESTfulなJSONインターフェースを提供しています。エンドポイントに対して画像のBase64文字列とキャプチャ種別コードをPOST送信し、レスポンスのJSONオブジェクトから認識結果を抽出する流れになります。
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class JsdCaptchaProcessor
{
private const string EndpointUrl = "https://v2-api.jsdama.com/upload";
private static readonly HttpClient Client = new();
public static async Task<string> ResolveAsync(string imageUrl, string user, string pass)
{
string base64Image = await ConvertImageToBase64Async(imageUrl);
var payload = new
{
captchaData = base64Image,
softwareId = 0,
softwareSecret = string.Empty,
username = user,
password = pass,
captchaType = 1001 // 英数字4桁などの標準タイプコード
};
Client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; CaptchaClient/1.0)");
var jsonPayload = JsonSerializer.Serialize(payload);
using var requestContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await Client.PostAsync(EndpointUrl, requestContent);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(responseJson);
var root = doc.RootElement;
if (!root.TryGetProperty("data", out JsonElement dataNode) ||
!dataNode.TryGetProperty("recognition", out JsonElement recNode))
{
throw new InvalidOperationException("認識結果の解析に失敗しました。");
}
return recNode.GetString()?.Trim() ?? string.Empty;
}
private static async Task<string> ConvertImageToBase64Async(string url)
{
using var httpClient = new HttpClient();
var bytes = await httpClient.GetByteArrayAsync(url);
return Convert.ToBase64String(bytes);
}
}
フォームエンコーディッド方式と署名認証(例:Fate型プラットフォーム)
一部のレガシーまたはセキュリティ要件の高いサービスでは、マルチパートではなく `application/x-www-form-urlencoded` 形式での送信が求められます。また、リプレイ攻撃防止のため、タイムスタンプとハッシュ署名の組み込みが必須となるケースが多いです。
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
public class FateCaptchaProcessor
{
private const string ApiEndpoint = "http://pred.fateadm.com/api/capreg";
// 実環境では設定ファイルやシークレットマネージャーから注入
private static readonly string PartnerId = System.Environment.GetEnvironmentVariable("FATE_PARTNER_ID");
private static readonly string PartnerKey = System.Environment.GetEnvironmentVariable("FATE_PARTNER_KEY");
private static readonly string AppIdentifier = System.Environment.GetEnvironmentVariable("FATE_APP_ID");
private static readonly string AppSecret = System.Environment.GetEnvironmentVariable("FATE_APP_KEY");
public static async Task<string> DecodeAsync(string imageUrl)
{
string base64Data = await ImageUtility.ToBase64StringAsync(imageUrl);
long currentTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
string partnerSignature = SecurityEngine.ComputeHmacSha256(PartnerId, PartnerKey, currentTimestamp);
string appSignature = SecurityEngine.ComputeHmacSha256(AppIdentifier, AppSecret, currentTimestamp);
var formData = new Dictionary<string, string>
{
["user_id"] = PartnerId,
["timestamp"] = currentTimestamp.ToString(),
["sign"] = partnerSignature,
["app_id"] = AppIdentifier,
["asign"] = appSignature,
["predict_type"] = "30400",
["img_data"] = base64Data
};
using var client = new HttpClient();
using var requestContent = new FormUrlEncodedContent(formData);
var httpResponse = await client.PostAsync(ApiEndpoint, requestContent);
httpResponse.EnsureSuccessStatusCode();
string rawJson = await httpResponse.Content.ReadAsStringAsync();
var apiResponse = JsonSerializer.Deserialize<ApiResponseModel>(rawJson);
if (apiResponse?.RspData == null)
throw new InvalidOperationException("応答データが不正です。");
var extraInfo = JsonSerializer.Deserialize<ExtraInfoModel>(apiResponse.RspData);
return extraInfo?.ResultCode?.Trim() ?? string.Empty;
}
}
public class ApiResponseModel
{
[JsonPropertyName("rsp_data")]
public string? RspData { get; set; }
}
public class ExtraInfoModel
{
[JsonPropertyName("result")]
public string? ResultCode { get; set; }
}
実装時の技術的注意点
本格的なプロダクション環境に組み込む場合は、以下のアーキテクチャ的考慮が必要です。
- 非同期パターンの徹底:
.Resultや.Wait()はASP.NETやUIスレッドでデッドロックを引き起こすため、必ずasync/awaitチェーンで処理します。 - クライアントの再利用:
HttpClientはインスタンスの作成・破棄を頻繁に行うとソケット枯渇(EXHAUSTION)の原因となります。 static なインスタンスまたはIHttpClientFactoryを通じて管理します。 - Base64変換の最適化: 画像URLをバイト配列に取得した後、
Convert.ToBase64String()で直接エンコードします。データURLスキーム(data:image/...)が含まれると一部のプラットフォームで拒否されるため、純粋なペイロードのみを渡します。 - レート制限とリトライ: サードパーティAPIは同時実行数や1分あたりの呼び出し回数に制限を設けるのが一般的です。指数関数的バックオフ(Exponential Backoff)を組み込んだリトライロジックと、サービス側からのステータスコード(例: 429 Too Many Requests)のハンドリングを必ず実装します。