Scarnox · Blog

Bộ giải captcha bằng C# với HttpClient: ví dụ .NET (2026)

Trong C#, bộ giải captcha là một lớp nhỏ bao quanh HttpClient. Bạn gửi sitekey và URL trang đến API của Scarnox, đọc token từ phản hồi JSON và submit nó vào form của website. Hướng dẫn này xây dựng lớp đó cho .NET 8, dành cho PopularCaptcha, loại mà Scarnox giải hôm nay theo kiểu proxyless.

Bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ và thử mã trên một trang bạn kiểm soát trước khi chạy nó ở bất kỳ nơi nào khác.

Yêu cầu

Tạo project

dotnet new console -n CaptchaDemo
cd CaptchaDemo

Không cần gói bổ sung nào. System.Net.Http và System.Text.Json là một phần của framework.

Lớp bộ giải

Lớp dưới đây sở hữu một HttpClient với header Bearer và thời gian chờ 185 giây. Nó trả về token, hoặc ném ngoại lệ kèm nội dung lỗi của 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;
        }

        // Lời gọi trả về khi token đã sẵn sàng. Không có polling.
        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; }
}

Dòng kiểm tra errorId là dòng quan trọng nhất. API trả về 0 khi thành công. Mọi giá trị khác đi kèm errorCode và errorDescription, và lớp này đưa cả hai lên.

Submit token

Token được gửi vào form của website. Hãy dùng một HttpClient thứ hai với một cookie container, để phiên đã tải form được tái sử dụng trong POST:

// 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 = "";

// Thử lại các lỗi tạm thời. Các lần giải thất bại không bị tính phí.
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 trước để site đặt cookie phiên.
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}");

Chạy nó:

set SCARNOX_API_KEY=scarnox_YOURAPIKEY
dotnet run

Trên PowerShell, dùng $env:SCARNOX_API_KEY = "scarnox_YOURAPIKEY" thay thế.

Vì sao được tổ chức như vậy

**Một HttpClient cho mỗi bộ giải.** HttpClient được thiết kế để tái sử dụng, không phải tạo mới cho mỗi lời gọi. Bộ giải sở hữu một instance và giải phóng nó khi kết thúc.

Thời gian chờ 185 giây. API bị chặn cho đến khi token sẵn sàng. Một thời gian chờ ngắn hơn sẽ hủy những lần giải lẽ ra đã gần xong.

Deserialize có kiểu. ReadFromJsonAsync ánh xạ JSON vào CreateTaskResponse, nên các kiểm tra lỗi và token đọc giống C# thông thường.

Submit form. Website nhận cả h-captcha-response lẫn g-recaptcha-response, vì các site đọc một trong hai. Cookie container giữ phiên giữa GET và POST.

Thời gian

Token hết hạn trong khoảng hai phút. Hãy giải ngay trước khi submit. Nếu bạn giải sớm trong một lô rồi submit muộn hơn nhiều, token sẽ cũ. Hết hạn token captcha trình bày chi tiết.

Widget ẩn

Truyền invisible: true vào SolveAsync khi widget là ẩn. Khi đó yêu cầu sẽ có is_invisible: true. Một số trang cần một callback JavaScript mà client phía máy chủ không thể kích hoạt. Trong trường hợp đó, công cụ tự động hóa trình duyệt là lựa chọn tốt hơn. Hướng dẫn captcha ẩn trình bày các mẫu thường gặp.

Thu thập dữ liệu có trách nhiệm

Nếu chương trình thu thập các trang công khai, hãy kiểm tra robots.txt và điều khoản của website trước. Dùng độ trễ giữa các yêu cầu, và chỉ giữ dữ liệu cá nhân bạn cần. Về nền tảng pháp lý, xem web scraping có hợp pháp không.

Giá

Scarnox tính phí theo mỗi lần giải thành công, và các lần giải thất bại không bị tính phí. Xem bảng giá hiện tại để biết mức phí. Bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ.

Các bước tiếp theo