Как решить GeeTest v3 в C#

solve geetest in c# - How to Solve GeeTest v3 in C# and Post the Result Back

GeeTest ломает ментальную модель, на которой построена большая часть кода для капчи. Здесь нет единого токена, который можно вставить в поле формы. Решение возвращает три значения, которые нужно отправлять обратно вместе, а капча, с которой вы начали, истекает примерно за минуту. Ошибитесь в любой из частей — и запрос завершится неудачей без полезного сообщения об ошибке.

Вот весь процесс на C#, включая те части, в которых легко ошибиться.

Два входных значения, и только одно из них стабильно

GeeTest v3 идентифицирует сайт с помощью gt и одна попытка с challenge. Они ведут себя совершенно по-разному.

ЗначениеПожизненноОткуда он берётся
gtСтатичен для сайта. Можно безопасно прописать в коде или кэшироватьОтвет инициализации GeeTest от сайта
challengeОдноразовый, истекает примерно через 60 секундТот же ответ init, каждый раз новый

Именно это истечение срока — самая частая причина сбоев с GeeTest. Если вы получаете пару, ставите задачу в очередь и решаете её через тридцать секунд после других задач, проверка может уже быть недействительной. Получайте её непосредственно перед решением и никогда из кэша.

Настройка

# Targets .NET Standard 2.0, so Framework 4.6.1+ and .NET 6-9 all work.
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, and this one covers GeeTest too

Обратите внимание, что GeeTest использует recaptchaTimeout, а не defaultTimeout. Последнее относится только к графическим капчам.

Решение

Три позиционных аргумента, в таком порядке:

var result = await solver.GeetestAsync(
    "81388ea1fc187e0c335c0a8907ff2625",   // gt, static per site
    "7cf6a8b1a2c34d5e6f7089abcdef0123",   // challenge, fetched seconds ago
    "https://example.com/login");

Console.WriteLine(result.Challenge);
Console.WriteLine(result.Validate);
Console.WriteLine(result.Seccode);

Эти три свойства и есть ответ. result.Code тоже заполняется, но для GeeTest в нём хранится сырая строка JSON, а не пригодный токен, поэтому обращаться к Code по привычке — это ошибка. На SolveResult, Challenge, Validate и Seccode относятся только к GeeTest и равны null для всех остальных типов.

Стоит знать: challenge который возвращается, не всегда тот же, что вы отправили. Используйте возвращённое значение, а не свой ввод.

Отправка ответа обратно

Отправьте все три ровно так, как это сделал бы собственный фронтенд сайта. Большинство интеграций GeeTest v3 используют эти имена полей, хотя сайт может их переименовать, поэтому проверьте реальную форму, прежде чем делать выводы:

using System.Net.Http;
using System.Collections.Generic;

using var http = new HttpClient();

var form = new FormUrlEncodedContent(new[]
{
    new KeyValuePair<string, string>("geetest_challenge", result.Challenge),
    new KeyValuePair<string, string>("geetest_validate",  result.Validate),
    new KeyValuePair<string, string>("geetest_seccode",   result.Seccode),
    new KeyValuePair<string, string>("username", "..."),
    new KeyValuePair<string, string>("password", "..."),
});

var response = await http.PostAsync("https://example.com/login", form);

Отправка двух из трёх или сочетание свежего validate с устаревшим challenge, отклоняется так же, как и неверный ответ.

Весь процесс по порядку

Получение пары и решение должны находиться рядом. Такая структура удерживает их вместе:

using System;
using System.Net.Http;
using System.Text.Json;
using CapSkip;

// 1. Fetch a fresh gt/challenge pair from the site's own init endpoint.
using var http = new HttpClient();
var initJson = await http.GetStringAsync(
    "https://example.com/geetest/init?t=" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());

using var doc = JsonDocument.Parse(initJson);
var gt        = doc.RootElement.GetProperty("gt").GetString();
var challenge = doc.RootElement.GetProperty("challenge").GetString();

// 2. Solve immediately. Do not queue this or await anything slow in between.
var result = await solver.GeetestAsync(gt, challenge, "https://example.com/login");

// 3. Post all three values together.
Console.WriteLine($"{result.Challenge} {result.Validate} {result.Seccode}");

Метка времени для сброса кеша в вызове init важнее, чем кажется. Эндпоинты init у GeeTest часто кешируются посредниками, и кешированный ответ выдаёт вам капчу, которая уже была использована.

Когда это не срабатывает

using System;
using CapSkip;

try
{
    var result = await solver.GeetestAsync(gt, challenge, pageUrl);
}
catch (CapSkip.ValidationException) { /* missing gt or challenge */ }
catch (NetworkException)            { /* CapSkip is not running */ }
catch (ApiException)                { /* API error, often a dead challenge */ }
catch (CapSkip.TimeoutException)    { /* exceeded recaptchaTimeout */ }
catch (CapSkipError)                { /* anything else from the SDK */ }

Qualify ValidationException и TimeoutException с помощью CapSkip пространство имён. Оба имени также существуют в System, и при импортированных обоих пространствах имён неполный catch привязывается к System типа и молча никогда не срабатывает. Перехват базового CapSkipError полностью обходит эту проблему.

На практике большинство сбоев GeeTest проявляются как ApiException и означают, что задача умерла до завершения решения. Решение — получать позже, а не повторять с теми же значениями.

Прокси и параллелизм

GeeTest — один из трёх типов, которые принимают прокси, наряду с reCAPTCHA и Turnstile. Графические капчи — нет, потому что они никогда не касаются целевого сайта.

using System.Collections.Generic;

var result = await solver.GeetestAsync(gt, challenge, pageUrl,
    new Dictionary<string, object?>
    {
        ["proxy"] = new Proxy("HTTPS", "user:[email protected]:3128"),
    });

Запуск нескольких параллельно работает, но каждому нужна своя, только что полученная пара. Не получайте пакет проверок заранее, чтобы затем решать их вместе, потому что последние из них истекут ещё до своей очереди.

// Correct: fetch and solve inside the same task.
var tasks = urls.Select(async url =>
{
    var (gt, challenge) = await FetchPairAsync(url);
    return await solver.GeetestAsync(gt, challenge, url);
});

var results = await Task.WhenAll(tasks);

Часто задаваемые вопросы

Почему result.Code не является пригодным токеном?

Потому что ответ GeeTest — это три значения, а не одно. Code сохраняет исходную строку JSON для полноты, тогда как SDK раскрывает полезные части в Challenge, Validate и Seccode. Используйте эти три.

Можно ли кэшировать капчу, чтобы сэкономить запрос?

Нет. Он одноразовый и истекает примерно через минуту. Его кэширование — самая частая причина, по которой интеграции GeeTest работают при тестировании и отказывают под нагрузкой: задержка в очереди сдвигает решение за пределы окна действия. gt значение безопасно кешировать.

Работает ли это для GeeTest v4?

The GeetestAsync метод предназначен для v3 — версии со слайдер-пазлом, построенной вокруг gt и challenge пара. v4 изменил модель параметров, поэтому сверьтесь с актуальной Документация по API относительно того, что поддерживается, прежде чем считать, что тот же вызов сработает.

Нужен ли мне прокси?

Только когда сайт чувствителен к геолокации или уже считает ваш адрес подозрительным. Если вы используете прокси, решайте и отправляйте по одному и тому же сетевому пути, иначе само несоответствие может вызвать повторную проверку.

Сводка

Получить gt и challenge непосредственно перед решением вызовите GeetestAsync с обоими плюс URL страницы, затем отправьте Challenge, Validate и Seccode воедино. Относитесь к проверке как к скоропортящейся, используйте возвращённую проверку, а не свои входные данные, и перехватывайте CapSkipError чтобы избежать конфликта пространств имён.

Сигнатуры методов для остальных языков находятся на Решение для GeeTest странице, полный интерфейс .NET находится на Решатель капчи на C# страница, и вы можете попробовать живую головоломку на нашей Демо GeeTest v3. CapSkip обрабатывает обход капчи локально, поэтому объём решений ничего не стоит за запрос.