C#における外部OCR APIを活用したキャプチャ画像の自動認識実装

サードパーティ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)のハンドリングを必ず実装します。

タグ: csharp captcha-automation httpclient third-party-api-integration ocr-systems

6月11日 16:48 投稿