Солвер капчі на C# з HttpClient: приклад для .NET (2026)
На C# солвер капчі — це невеликий клас навколо HttpClient. Ви надсилаєте sitekey і URL сторінки до API Scarnox, зчитуєте токен із JSON-відповіді і відправляєте його у форму сайту. Цей посібник будує такий клас для .NET 8 для PopularCaptcha, яку Scarnox сьогодні розв'язує в режимі proxyless.
Почніть з безкоштовного пробного кредиту, картка не потрібна і спробуйте код на сторінці, якою володієте, перш ніж запускати його на чомусь іншому.
Вимоги
- .NET 8 SDK (
dotnet --version). - Ключ API Scarnox, який зчитується зі змінної середовища.
- Sitekey віджета PopularCaptcha на сторінці. Як його знайти, дивіться в посібнику зі пошуку sitekey капчі.
Створення проєкту
dotnet new console -n CaptchaDemo
cd CaptchaDemo
Додаткові пакети не потрібні. System.Net.Http і System.Text.Json входять до складу фреймворку.
Клас солвера
Клас нижче володіє одним HttpClient з заголовком Bearer і таймаутом 185 секунд. Він повертає токен або викидає виключення з текстом помилки від 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, і клас передає обидва.
Відправка токена
Токен іде у форму сайту. Використовуйте другий HttpClient з контейнером cookie, щоб сесія, яка завантажила форму, використовувалася і при 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 = "";
// Повторюємо тимчасові збої. Невдалі розв'язання не тарифікуються.
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 на солвер.** HttpClient призначений для повторного використання, а не для створення при кожному виклику. Солвер володіє одним клієнтом і звільняє його в кінці.
Таймаут 185 секунд. API блокується, доки токен не буде готовий. Коротший таймаут скасовує розв'язання, які мали от-от завершитися.
Типізована десеріалізація. ReadFromJsonAsync відображає JSON на CreateTaskResponse, тож перевірки помилок і токена читаються як звичайний C#.
Відправка форми. Сайт отримує і h-captcha-response, і g-recaptcha-response, бо сайти читають одне з них. Контейнер cookie зберігає сесію між GET і POST.
Час
Токен спливає приблизно за дві хвилини. Розв'язуйте безпосередньо перед відправкою. Якщо ви розв'язали заздалегідь у пакетній обробці і відправили значно пізніше, токен застаріє. Посібник про термін дії токена капчі розглядає деталі.
Невидимі віджети
Передайте invisible: true у SolveAsync, коли віджет невидимий. Тоді запит містить is_invisible: true. Деякі сторінки потребують JavaScript-колбеку, який серверний клієнт не може викликати. У такому разі краще підійде інструмент автоматизації браузера. Посібник про невидиму капчу описує патерни.
Скрапінг відповідально
Якщо програма скрапить публічні сторінки, спершу перевірте robots.txt і умови сайту. Використовуйте затримку між запитами і зберігайте лише ті персональні дані, які вам потрібні. Щодо правового контексту дивіться чи законний веб-скрапінг.
Ціни
Scarnox тарифікує за кожне успішне розв'язання, а невдалі розв'язання безкоштовні. Тарифи дивіться на сторінці цін. Почніть з безкоштовного пробного кредиту, картка не потрібна.