Как решить GeeTest v3 на Python и отправить результат обратно

Большинство кода для капчи предполагает: один токен на входе, один на выходе. GeeTest это нарушает. Решение возвращает три значения, которые нужно отправлять вместе, а капча, которую вы передали, истекает примерно через шестьдесят секунд после того, как вы её получили. И то, и другое сбивает людей с толку, и ни одно из них не выдаёт понятного сообщения об ошибке.
Два входных значения, ведущих себя по-разному
| Значение | Пожизненно |
|---|---|
gt | Статичен для сайта. Кэшируйте его свободно |
challenge | Одноразовый, недействителен примерно через 60 секунд |
Оба берутся из собственного init-эндпоинта GeeTest на сайте. gt фактически является идентификатором сайта. challenge выдаётся на каждую попытку, и использование его как многоразового становится самой распространённой причиной, по которой интеграция GeeTest работает при тестировании и разваливается под нагрузкой.
Настройка
# Python 3.10 or newer. pip install capskip
from capskip import CapSkip
solver = CapSkip(
host="127.0.0.1",
port=8080,
recaptchaTimeout=300, # GeeTest uses this, not defaultTimeout
)Это различие в тайм-аутах легко упустить. defaultTimeout управляет только капчами-картинками; всё интерактивное использует recaptchaTimeout.
Распознавание
result = solver.geetest(
gt="81388ea1fc187e0c335c0a8907ff2625",
challenge="7cf6a8b1a2c34d5e6f7089abcdef0123",
url="https://example.com/login",
)
print(result["challenge"])
print(result["validate"])
print(result["seccode"])Эти три ключа и есть ответ. result["code"] тоже заполняется, но для GeeTest оно содержит необработанную JSON-строку, а не то, что можно отправить, поэтому обращение к нему по привычке приводит к непонятному сбою.
Используйте challenge который возвращается, а не тот, что вы отправили. Это не всегда одно и то же значение.
Полный процесс в одном месте
Получение и решение должны идти вплотную. Любая задержка между ними рискует истечением срока:
import time
import requests
from capskip import CapSkip
solver = CapSkip()
LOGIN = "https://example.com/login"
# 1. Fresh pair, cache-busted. Cached init responses hand back dead challenges.
init = requests.get(
"https://example.com/geetest/init",
params={"t": int(time.time() * 1000)},
).json()
# 2. Solve immediately. Do not queue this.
result = solver.geetest(gt=init["gt"], challenge=init["challenge"], url=LOGIN)
# 3. Post all three together, alongside the real form fields.
response = requests.post(LOGIN, data={
"geetest_challenge": result["challenge"],
"geetest_validate": result["validate"],
"geetest_seccode": result["seccode"],
"username": "...",
"password": "...",
})Временная метка для обхода кэша важнее, чем кажется. Эндпоинты инициализации GeeTest часто кэшируются CDN или прокси, и кэшированный ответ выдаёт вам капчу, которая уже была использована.
Эти три имени полей отражают обычное соглашение GeeTest v3, но сайт может их переименовать. Проверьте реальную форму, прежде чем делать предположения.
Выполнение нескольких сразу
Ловушка здесь кроется в неправильной пакетной обработке. Если получить кучу заданий, а затем решать их все, поздние гарантированно истекут до своей очереди. Получайте и решайте в рамках одной задачи:
import asyncio
from capskip import AsyncCapSkip
async def solve_one(solver, url):
init = await fetch_pair(url) # your own fetch, awaited
return await solver.geetest(
gt=init["gt"], challenge=init["challenge"], url=url,
)
async def main():
solver = AsyncCapSkip()
return await asyncio.gather(*[solve_one(solver, u) for u in urls])
asyncio.run(main())Python остаётся единственным SDK CapSkip, где AsyncCapSkip представляет собой настоящий асинхронный клиент, а не псевдоним, поэтому он действительно выполняет решения параллельно.
Когда это не срабатывает
from capskip import (
ValidationException, NetworkException, ApiException, TimeoutException,
)
try:
result = solver.geetest(gt=gt, challenge=challenge, url=page_url)
except ValidationException:
pass # missing gt or challenge
except NetworkException:
pass # CapSkip is not running
except ApiException:
pass # usually a challenge that expired before the solve finished
except TimeoutException:
pass # exceeded recaptchaTimeoutНа практике большинство сбоев GeeTest приходят в виде ApiException и означают, что задача умерла. Решение состоит в том, чтобы получать позже, а не повторять с той же парой, потому что израсходованная задача никогда снова не становится действительной.
Часто задаваемые вопросы
Почему result[“code”] нельзя использовать?
Потому что ответ состоит из трёх значений, а не из одного. code сохраняет необработанный JSON для полноты, тогда как SDK разворачивает полезные части в challenge, validate и seccode. Отправьте эти три.
Можно ли кэшировать задачу между запусками?
Нет. Он одноразовый и истекает примерно через минуту. gt безопасно кэшировать; капчу кэшировать нельзя никогда.
Охватывает ли это GeeTest v4?
The geetest метод предназначен для v3, слайдер-пазла, построенного вокруг gt и challenge пара. v4 изменил модель параметров, поэтому сверьтесь с актуальной Документация по API прежде чем предполагать, что применяется тот же вызов.
Сводка
Получить gt и challenge непосредственно перед решением с помощью запроса без кэша вызовите geetest, затем отправьте challenge, validate и seccode вместе. Используйте возвращённый challenge, а не свой ввод, и никогда не запрашивайте challenge пакетами заранее, до решения.
Другие языки доступны на Решение для GeeTest страница, более широкий набор возможностей Python на Решатель капчи для Python страница, и есть «живая» головоломка на нашей Демо GeeTest v3. CapSkip обрабатывает обход капчи локально, поэтому объём решений бесплатен.
