Как решать страницы-проверки Cloudflare Turnstile в C#

Cloudflare Turnstile встречается в двух видах, и код на C# для каждого из них отличается. Виджету, встроенному в форму, нужны только sitekey и URL. Полностраничной промежуточной проверке (interstitial) требуются два дополнительных значения, извлечённых со страницы, и она будет принята, только если вы вернёте тот же user agent, который использовал solver. Пропустите этот последний момент — и получите токен, который выглядит идеально, но каждый раз не проходит валидацию.
В этом руководстве разобраны оба варианта с рабочим кодом .NET.
Виджет или страница проверки?
Определите, какой из них перед вами, прежде чем писать какой-либо код.
| Режим виджета | Страница проверки | |
|---|---|---|
| Что вы видите | Флажок внутри формы, с которой вы всё ещё можете взаимодействовать | Полноэкранная промежуточная страница, обычно “Checking your browser” |
| Остальная часть страницы | Загружается нормально | Заблокировано, пока проверка не пройдена |
Требуется data / pagedata | Нет | Да |
| Требуется возвращённый user agent | Нет | Да |
Если вы не уверены, наше живая демо-страница Turnstile запускает вариант виджета, так что вы можете сравнить его с тем, с чем реально сталкиваетесь.
Настройка
CapSkip работает на вашей машине, поэтому перед всем этим нужно запустить локальный сервис. Установите SDK из NuGet:
# .NET Standard 2.0, so this works on .NET Framework 4.6.1+, # .NET Core 2.0+, and .NET 6 through 9. dotnet add package CapSkip
Затем направьте клиент на порт, указанный в приложении CapSkip:
using CapSkip;
var solver = new CapSkipClient(
apiKey: "capskip", // any string when key validation is off
host: "127.0.0.1",
port: 8080,
recaptchaTimeout: 300); // seconds, also covers Turnstile and GeeTestРежим виджета — простой случай
Два аргумента — и готово:
var result = await solver.TurnstileAsync(
"0x4AAAAAAA...", // sitekey from the data-sitekey attribute
"https://example.com/login");
Console.WriteLine(result.Code); // cf-turnstile-response tokenПеретащите result.Code в cf-turnstile-response поле и отправьте форму. Больше ничего делать не нужно.
Страницам проверки нужны ещё два значения
Промежуточная проверка (interstitial) несёт состояние, привязанное к конкретному запросу, с которым связан токен. Две его части должны передаваться вместе с вашим запросом на решение:
- cData, передаётся как
data - chlPageData, передаётся как
pagedata
Оба встроены в саму страницу проверки, а не в атрибут формы, поэтому сначала нужно считать страницу, а уже потом решать капчу. На стандартной промежуточной странице Cloudflare они находятся в собственном объекте параметров проверки страницы, рядом с sitekey.
Они также одноразовые и привязаны к конкретной загрузке страницы. Получайте их, решайте сразу и не кешируйте их между попытками.
using System.Collections.Generic;
using CapSkip;
var result = await solver.TurnstileAsync(
sitekey,
pageUrl,
new Dictionary<string, object?>
{
["data"] = cData, // the cData value from the page
["pagedata"] = chlPageData, // the chlPageData value
["action"] = "managed", // optional, when the page declares one
});
Console.WriteLine(result.Code);
Console.WriteLine(result.UserAgent); // you are going to need thisТо, что все упускают: user agent
Turnstile привязывает токен к браузерному фингерпринту, который его создал, а user agent — его часть. CapSkip возвращает тот, который он использовал, в result.UserAgent. Если затем вы отправите токен с HttpClient отправляя свой собственный user agent по умолчанию, значения не совпадают, и Cloudflare отклоняет токен, который в остальном полностью действителен.
UserAgent заполняется только для Turnstile. Для всех остальных типов капчи оно равно null, поэтому это сбивает с толку тех, кто повторно использует рабочий помощник для reCAPTCHA.
using System.Net.Http;
using CapSkip;
var result = await solver.TurnstileAsync(sitekey, pageUrl, options);
using var http = new HttpClient();
// Send back the exact user agent the solve was performed with.
http.DefaultRequestHeaders.UserAgent.ParseAdd(result.UserAgent);
var form = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("cf-turnstile-response", result.Code),
});
var response = await http.PostAsync(pageUrl, form);Если ваш токен отклоняется, а вариант с истёкшим сроком вы уже исключили cData, это почти всегда и есть причина.
Правильная обработка сбоев
Каждое исключение SDK наследуется от CapSkipError, поэтому вы можете перехватывать один этот тип или обрабатывать каждый по отдельности. Есть одна специфичная для .NET ловушка, о которой стоит знать.
using System;
using CapSkip;
try
{
var result = await solver.TurnstileAsync(sitekey, pageUrl, options);
}
catch (CapSkip.ValidationException) { /* bad parameters */ }
catch (NetworkException) { /* CapSkip is not running */ }
catch (ApiException) { /* API returned an error code */ }
catch (CapSkip.TimeoutException) { /* exceeded recaptchaTimeout */ }
catch (CapSkipError) { /* anything else from the SDK */ }TimeoutException и ValidationException существуют в обоих System и CapSkip. Когда импортированы оба пространства имён, неквалифицированный catch (TimeoutException) разрешается в System один и молча никогда не срабатывает. Уточняйте их, как указано выше, или просто перехватывайте CapSkipError и изучите его.
Решение нескольких сразу
Каждый метод возвращает Task, поэтому применяется обычная многопоточность .NET:
var results = await Task.WhenAll(
solver.TurnstileAsync(sitekeyA, "https://a.example.com"),
solver.TurnstileAsync(sitekeyB, "https://b.example.com"));
foreach (var r in results)
{
Console.WriteLine(r.Code);
}SDK также экспортирует AsyncCapSkip, но в .NET это лишь псевдоним для CapSkipClient. Он существует для того, чтобы код, перенесённый из Python SDK, продолжал компилироваться. Переход на него ничего вам не даёт, потому что ввод-вывод в .NET уже асинхронный.
Маршрутизация через прокси
Если challenge чувствителен к геолокации, решайте его через тот же сетевой путь, с которого будете отправлять:
var result = await solver.TurnstileAsync(sitekey, pageUrl,
new Dictionary<string, object?>
{
["data"] = cData,
["pagedata"] = chlPageData,
["proxy"] = new Proxy("HTTPS", "user:[email protected]:3128"),
});Прокси поддерживаются для Turnstile, reCAPTCHA и GeeTest. Для капч-картинок они не поддерживаются, так как те решаются из байтов изображения и никогда не обращаются к целевому сайту.
Часто задаваемые вопросы
Всегда ли мне нужны cData и chlPageData?
Нет. Только для полностраничных промежуточных проверок (interstitial). Виджету Turnstile внутри формы не нужно ничего, кроме sitekey и URL страницы, а передача пустых значений виджету скорее приведёт к сбою решения, чем поможет.
Мой токен действителен, но сайт всё равно его отклоняет. Почему?
Почти всегда дело в user agent. Turnstile привязывает токен к фингерпринту, который его создал, поэтому вы должны отправлять запрос, используя значение в result.UserAgent. Вторая по частоте причина — устаревший cData, который привязан к одной загрузке страницы.
Работает ли это на .NET Framework?
Да. Пакет ориентирован на .NET Standard 2.0, поэтому он работает на .NET Framework 4.6.1 и новее, .NET Core 2.0+ и всех современных выпусках .NET. Из синхронного кода вы можете вызвать .GetAwaiter().GetResult(), хотя правильнее использовать await.
Сколько времени должно занимать решение Turnstile?
Обычно несколько секунд. Клиент опрашивает внутренне, начиная с 250 мс и увеличивая интервал до pollingInterval, и прекращает попытки на recaptchaTimeout которое по умолчанию составляет 300 секунд. Если вы постоянно упираетесь в этот предел, проверьте, что сервис CapSkip действительно запущен, вместо того чтобы увеличивать тайм-аут.
Сводка
Виджет Turnstile — это вызов с двумя аргументами. Страницы-проверки требуют data и pagedata считывается заново со страницы, и токен должен отправляться вместе с user agent, возвращённым вместе с ним. Перехватите CapSkipError вместо того чтобы бороться с конфликтом пространств имён, и используйте прокси, когда капча чувствительна к геолокации.
Полный набор методов для .NET находится на Решатель капчи на C# странице, справочник по параметрам находится в Документация по API, и Поддержка Turnstile охватывает остальные языки. Сам CapSkip является безлимитный сервис распознавания капчи который работает локально, поэтому ничего из перечисленного не тарифицируется за каждое решение.
