Введение в предмет — или почему распознавание капчи Geetest не похоже на новый Haval?
В последнее время китайские товары и сервисы можно встретить практически в любой нише. Да, когда ты слышишь, что это китайская разработка, с улыбкой вспоминаешь 90-е и знаменитые ролики в интернете «Очки н‑н-надо?», и в большинстве случаев мало что изменилось, даже вон DeepSeek по итогу получился не совсем Deep и не до конца Seek. Но кое-что у них все же получилось, и получилось так, что многие оптимизаторы глотают соленые слезы, пытаясь обойти Geetest капчу.

Почему же китайские братушки так сильно забили на импротный автопром, и так сильно заморочились с сервисом для защиты от спама? Есть мнение (оно конечно субъективное, но все же мнение) — просто в случае с GeeTest — его юзают не только на импорт, но и на внутреннем рынке, а для себя делают они умеют.
Вот официальное определение — Geetest CAPTCHA — это современная система защиты, широко применяемая на различных веб-сервисах для предотвращения автоматизированных запросов. В основе её работы лежит динамичный слайдер с пазлом, где пользователю требуется перетащить фрагмент изображения в нужное место.
Мне стало интересно разобраться в этой капче подробнее и описать некоторые подводные камни, которые могут встретиться при ее обходе, и на что следует обратить внимание при написании капча решателя.
Давайте разбираться более подробно.
Сразу небольшая ремарка — обходить Geetest CAPTCHA я буду исключительно через сервис распознавания капчи (я использую 2капча)
Принципы работы Geetest CAPTCHA — решение капчи Geetest, которое растрогает даже видавшего виды разработчика
Geetest CAPTCHA представляет собой двухкомпонентную защиту — собственно сама капча слайдер, которая включает в себя:
Динамическую генерацию изображений
При каждом запросе сервер генерирует уникальный фон с «дыркой» и изображение-пазл. Это усложняет применение заранее подготовленных решений.Интерактивный слайдер
Пользователь должен перетащить пазл так, чтобы он совпал с вырезанной областью. При этом система фиксирует:Конечное положение пазла.
Траекторию движения слайдера.
Временные интервалы между действиями.
Второй компонент защиты — сбор и анализ поведенческих данных, в целом этот компонент не существует отдельно от капчи, он действует на всех этапах распознавания: Как пользователь двигал мышкой, как происходило перетаскивание, дрожание курсора и т. п. мелкие детали, на которые не всегда обращаешь внимание.
Ну и наконец — валидация сервером
После завершения перетаскивания браузер отправляет на сервер данные о движении и позиционировании, которые сравниваются с ожидаемыми параметрами.
Такой многоуровневый подход позволяет усложнить имитацию действий робота и усложнить автоматический обход Geetest капчи.
Описанная выше технология характерна для 4-ой версии Geetest (GeeTest V4), тогда как у его младшего собрата Geetest v3 отсутсвовала возможность проведения проверки в «невидимом» режиме, и была более простая система поведенческого анализа.
По факту же, что 4, что 3 версии Geetest капчи не особо просты для автоматизаторов, и хуже поддаются обходу, чем та же рекапча (благо гитест не так распространен в Европе).
Особенности Geetest CAPTCHA в чем тонкости и почему решить эту капчу на автомате не самая простая задача?
Для распознавания, скажем, reCAPTCHA, требуется отыскать на странице, где расположена капча определенные параметры, передать их на сервис распознавания капчи и дождаться решения. Причем эти параметры не динамические, а статические. Статика сильно облегчает процесс распознавания. Да, есть другие параметры, которые могут осложнить жизнь, при распознавании reCAPTCHA, но на уровне найти на странице и передать на сервис, все вполне себе прозаично.

С Geetest CAPTCHA не все так однозначно… Там есть и старая добрая статика и динамика, которую нужно находить каждый раз, когда меняется капча.
Рассмотрим на примере Geetest v3 и Geetest v4
Geetest v3
– статичные параметры, которые требуются для корректного распознавания капчи:
• websiteURL – URL страницы с капчей.
• gt – значение.
Динамический параметр:
• challenge – генерируется при загрузке страницы (его необходимо получать каждый раз заново, иначе капча будет считаться недействительной).
Geetest v4
Вместо отдельных параметров gt и challenge тут используется объект initParameters, который обязательно должен содержать captcha_id – идентификатор конфигурации капчи для сайта.
С технической точки зрения, вроде как все просто, но не забываем о том, что все эти параметры находятся не html странице, где расположена капча, а генерятся после того, как с капчей начинают взаимодействовать, то, есть дополнительно к поиску нужных параметров, подключается необходимость эмуляции действий пользователя, что в свою очередь может стать красным флагом для GeeTest, так что тут в большинстве случаев могут потребоваться прокси.
В общем, каждая новая необходимость, тянет за собой еще одну и еще одну. Я буду пробовать обойти GeeTest CAPTCHA на тестовой странице сервиса, там не сильно она сложная и должно все получится без прокси, но не забывайте, что они могут понадобиться вам.
Подготовка к реализации обхода капчи Geetest
Краткий экскурс в техническую составляющую капчи я провел, пора переходить к основной части - как же ее обойти.
Для начала нудное введение, что нужно для обхода Geetest CAPTCHA:
Python 3
Зайдите на официальный сайт python.org и скачайте установщик для вашей операционной системы.
Следуйте инструкциям установщика, убедившись, что опция добавления Python в PATH активирована.
Пакетный менеджер pip
pip обычно устанавливается вместе с Python.
Как проверить:
Откройте командную строку (или терминал) и выполните команду:
pip --version
Необходимые библиотеки Python: requests и selenium
Что нужно:
Две внешние библиотеки:requests – для выполнения HTTP-запросов к API 2Captcha.
selenium – для управления браузером (Chrome) и автоматизации взаимодействия с веб-страницей.
Как установить:
Выполните в командной строке:
pip install requests selenium
ChromeDriver
ChromeDriver – это отдельное приложение, которое позволяет Selenium управлять Google Chrome.
Как установить:
Определите версию Chrome:
В браузере откройте меню «О Chrome» (обычно в разделе «Справка» или «О программе») и узнайте текущую версию браузера.Скачайте соответствующую версию ChromeDriver:
Перейдите на официальный сайт ChromeDriver и скачайте версию, которая соответствует вашей версии Chrome.
Настройте PATH:
Распакуйте скачанный архив и поместите исполняемый файл chromedriver в папку, которая находится в системной переменной PATH. Либо укажите путь к нему в настройках Selenium в коде, например:
driver = webdriver.Chrome(executable_path='/путь/до/chromedriver', options=options)
Еще понадобится ключ АПИ от сервиса распознавания GeetTest CAPTCHA, ниже расскажу куда его нужно будет подставить.
Сразу приведу полный текст скрипта, а уже ниже опишу что он делает и как:
import re
import time
import json
import argparse
import requests
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Замените на ваш реальный API-ключ 2Captcha
API_KEY = "Ваш ключ АПИ"
# Эндпоинты 2Captcha API
CREATE_TASK_URL = "https://api.2captcha.com/createTask"
GET_TASK_RESULT_URL = "https://api.2captcha.com/getTaskResult"
def extract_geetest_v3_params(html):
"""
Пытаемся извлечь параметры для GeeTest V3 (gt и challenge) из HTML.
(Используется, если параметры доступны в коде страницы)
"""
gt_match = re.search(r'["\']gt["\']\s*:\s*["\'](.*?)["\']', html)
challenge_match = re.search(r'["\']challenge["\']\s*:\s*["\'](.*?)["\']', html)
gt = gt_match.group(1) if gt_match else None
challenge = challenge_match.group(1) if challenge_match else None
return gt, challenge
def extract_geetest_v4_params(html):
"""
Извлекает captcha_id для GeeTest V4 из HTML.
Ищем строку вида: captcha_id=<32 шестнадцатеричных символов>
Если после captcha_id попадают лишние символы, они отбрасываются.
"""
match = re.search(r'captcha_id=([a-f0-9]{32})', html)
if match:
return match.group(1)
match = re.search(r'captcha_id=([^&"\']+)', html)
if match:
captcha_id_raw = match.group(1)
captcha_id = captcha_id_raw.split("<")[0]
return captcha_id.strip()
return None
def get_geetest_v3_params_via_requests(website_url):
"""
Для демо-страницы GeeTest V3 возвращаем статичные параметры,
как указано в инструкциях (в примерах PHP, Java, Python).
Это устранит ошибку, когда попытка split() возвращает весь HTML.
"""
gt = "f3bf6dbdcf7886856696502e1d55e00c"
challenge = "12345678abc90123d45678ef90123a456b"
return gt, challenge
def auto_extract_params(website_url):
"""
Если URL содержит "geetest-v4" – работаем с V4 (с использованием Selenium для извлечения captcha_id).
Если URL содержит "geetest" (без -v4) – считаем, что это GeeTest V3 и берем параметры через GET (статичные для демо).
Возвращает кортеж: (driver, version, gt, challenge_or_captcha_id)
"""
if "geetest-v4" in website_url:
options = Options()
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
driver = webdriver.Chrome(options=options)
driver.get(website_url)
time.sleep(3)
try:
wait = WebDriverWait(driver, 10)
element = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "#embed-captcha .gee-test__placeholder"))
)
driver.execute_script("arguments[0].click();", element)
time.sleep(5)
except Exception as e:
print("Ошибка при загрузке виджета для V4:", e)
html = driver.page_source
captcha_id = extract_geetest_v4_params(html)
return driver, "4", None, captcha_id
elif "geetest" in website_url:
# Для GeeTest V3 демо-страницы используем статичные параметры
gt, challenge = get_geetest_v3_params_via_requests(website_url)
options = Options()
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
driver = webdriver.Chrome(options=options)
driver.get(website_url)
return driver, "3", gt, challenge
else:
return None, None, None, None
def create_geetest_v3_task(website_url, gt, challenge, proxyless=True, proxy_details=None):
"""
Создает задачу для GeeTest V3 через 2Captcha API.
Обязательные параметры: websiteURL, gt, challenge.
"""
task_type = "GeeTestTaskProxyless" if proxyless else "GeeTestTask"
task = {
"type": task_type,
"websiteURL": website_url,
"gt": gt,
"challenge": challenge
}
if not proxyless and proxy_details:
task.update(proxy_details)
payload = {
"clientKey": API_KEY,
"task": task
}
response = requests.post(CREATE_TASK_URL, json=payload)
return response.json()
def create_geetest_v4_task(website_url, captcha_id, proxyless=True, proxy_details=None):
"""
Создает задачу для GeeTest V4 через 2Captcha API.
Обязательные параметры: websiteURL, версия (4) и initParameters с captcha_id.
"""
task_type = "GeeTestTaskProxyless" if proxyless else "GeeTestTask"
task = {
"type": task_type,
"websiteURL": website_url,
"version": 4,
"initParameters": {
"captcha_id": captcha_id
}
}
if not proxyless and proxy_details:
task.update(proxy_details)
payload = {
"clientKey": API_KEY,
"task": task
}
response = requests.post(CREATE_TASK_URL, json=payload)
return response.json()
def get_task_result(task_id, retry_interval=5, max_retries=20):
"""
Опрашивает 2Captcha API до получения результата.
"""
payload = {
"clientKey": API_KEY,
"taskId": task_id
}
for i in range(max_retries):
response = requests.post(GET_TASK_RESULT_URL, json=payload)
result = response.json()
if result.get("status") == "processing":
print(f"Капча ещё не решена, ждем... {i+1}")
time.sleep(retry_interval)
else:
return result
return {"errorId": 1, "errorDescription": "Timeout waiting for solution."}
def main():
parser = argparse.ArgumentParser(
description="Решение GeeTest CAPTCHA через 2Captcha API с автоматическим извлечением параметров"
)
parser.add_argument("--website-url", required=True, help="URL страницы с капчей")
# Опциональные параметры для использования прокси
parser.add_argument("--proxy-type", help="Тип прокси (http, socks4, socks5)")
parser.add_argument("--proxy-address", help="IP-адрес прокси-сервера")
parser.add_argument("--proxy-port", type=int, help="Порт прокси-сервера")
parser.add_argument("--proxy-login", help="Логин для прокси (если требуется)")
parser.add_argument("--proxy-password", help="Пароль для прокси (если требуется)")
args = parser.parse_args()
proxyless = True
proxy_details = {}
if args.proxy_type and args.proxy_address and args.proxy_port:
proxyless = False
proxy_details = {
"proxyType": args.proxy_type,
"proxyAddress": args.proxy_address,
"proxyPort": args.proxy_port
}
if args.proxy_login:
proxy_details["proxyLogin"] = args.proxy_login
if args.proxy_password:
proxy_details["proxyPassword"] = args.proxy_password
print("Загружаем страницу:", args.website_url)
driver, version, gt, challenge_or_captcha_id = auto_extract_params(args.website_url)
if driver is None or version is None:
print("Не удалось получить страницу или извлечь параметры.")
return
print("Определена версия GeeTest:", version)
if version == "3":
if not gt or not challenge_or_captcha_id:
print("Не удалось извлечь параметры gt и challenge для GeeTest V3.")
driver.quit()
return
print("Используем параметры для GeeTest V3:")
print("gt =", gt)
print("challenge =", challenge_or_captcha_id)
create_response = create_geetest_v3_task(
website_url=args.website_url,
gt=gt,
challenge=challenge_or_captcha_id,
proxyless=proxyless,
proxy_details=proxy_details
)
elif version == "4":
captcha_id = challenge_or_captcha_id
if not captcha_id:
print("Не удалось извлечь captcha_id для GeeTest V4.")
driver.quit()
return
print("Используем captcha_id для GeeTest V4:", captcha_id)
create_response = create_geetest_v4_task(
website_url=args.website_url,
captcha_id=captcha_id,
proxyless=proxyless,
proxy_details=proxy_details
)
else:
print("Неизвестная версия:", version)
driver.quit()
return
if create_response.get("errorId") != 0:
print("Ошибка при создании задачи:", create_response.get("errorDescription"))
driver.quit()
return
task_id = create_response.get("taskId")
print("Задача создана. Task ID:", task_id)
print("Ожидаем решение капчи...")
result = get_task_result(task_id)
if result.get("errorId") != 0:
print("Ошибка при получении результата:", result.get("errorDescription"))
driver.quit()
return
solution = result.get("solution")
print("Капча решена. Полученное решение:")
print(json.dumps(solution, indent=4))
# Подставляем полученные данные в страницу
if version == "3":
# Для GeeTest V3 ожидаются поля: challenge, validate, seccode
js_script = """
function setOrUpdateInput(id, value) {
var input = document.getElementById(id);
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.id = id;
input.name = id;
document.getElementById('geetest-demo-form').appendChild(input);
}
input.value = value;
}
setOrUpdateInput('geetest_challenge', arguments[0]);
setOrUpdateInput('geetest_validate', arguments[1]);
setOrUpdateInput('geetest_seccode', arguments[2]);
document.querySelector('#embed-captcha').innerHTML =
'<div style="padding:20px; background-color:#e0ffe0; border:2px solid #00a100; font-size:18px; color:#007000; text-align:center;">' +
'Капча успешно пройдена!<br>' +
'challenge: ' + arguments[0] + '<br>' +
'validate: ' + arguments[1] + '<br>' +
'seccode: ' + arguments[2] +
'</div>';
"""
challenge_sol = solution.get("challenge")
validate_sol = solution.get("validate")
seccode_sol = solution.get("seccode")
driver.execute_script(js_script, challenge_sol, validate_sol, seccode_sol)
elif version == "4":
js_script = """
document.querySelector('#embed-captcha').innerHTML =
'<div style="padding:20px; background-color:#e0ffe0; border:2px solid #00a100; font-size:18px; color:#007000; text-align:center;">Капча V4 успешно пройдена!</div>';
"""
driver.execute_script(js_script)
print("Результат подставлен в страницу. Браузер будет открыт на 30 секунд для визуальной проверки.")
time.sleep(30)
driver.quit()
if __name__ == "__main__":
main()
Что делает скрипт:
Импорт библиотек и определение констант:
Импорт модулей:
Скрипт использует стандартные модули Python — re для работы с регулярными выражениями, time для задержек, json для форматирования данных, argparse для обработки аргументов командной строки, а также requests для отправки HTTP-запросов. Для автоматизации браузера применяется библиотека Selenium с импортом необходимых классов (например, для настройки опций Chrome, ожидания элементов и работы с элементами страницы).Константы:
Определён API-ключ 2Captcha (API_KEY) и URL-адреса API для создания задачи (CREATE_TASK_URL
) и получения результата (GET_TASK_RESULT_URL
). Эти параметры используются для взаимодействия со службой 2Captcha.
Функции для извлечения параметров капчи:
extract_geetest_v3_params(html):
Принимает HTML-код страницы и с помощью регулярных выражений пытается найти значения параметровgt
иchallenge
, которые требуются для GeeTest V3. Если соответствующие строки найдены, функция возвращает их значения.extract_geetest_v4_params(html):
Извлекает параметр captcha_id для GeeTest V4. Сначала пытается найти строку, содержащую 32 шестнадцатеричных символа после меткиcaptcha_id
. Если это не удаётся, используется альтернативный шаблон с последующим отбрасыванием лишних символов.
Автоматическое определение версии капчи и извлечение параметров (auto_extract_params):
Функция получает URL страницы и определяет, какую версию GeeTest использовать:
GeeTest V4:
Если URL содержит подстроку"geetest-v4"
, то скрипт:Инициализирует браузер Chrome с отключённым GPU и в режиме без песочницы.
Загружает страницу.
Использует
WebDriverWait
для ожидания появления элемента с CSS-селектором#embed-captcha
.gee-test__placeholder
.Выполняет клик по элементу, чтобы инициировать загрузку капчи.
Ждёт несколько секунд для загрузки виджета.
Получает исходный HTML и извлекает
captcha_id
с помощью функцииextract_geetest_v4_params
.Возвращает объект драйвера, версию "4", значение для
gt
(в данном случаеNone
) и извлечённыйcaptcha_id
.
GeeTest V3:
Если URL содержит подстроку "geetest
" (но не "geetest-v4
"), то:Получаются статичные параметры
gt
иchallenge
с помощью функцииget_geetest_v3_params_via_requests
.Инициализируется браузер Chrome с аналогичными настройками.
Загружается страница.
Возвращается драйвер, версия "3", а также значения
gt
иchallenge
.
Если ни одно условие не выполнено, функция возвращает
None
для всех значений.
Создание задач для решения капчи через 2Captcha API:
create_geetest_v3_task(website_url, gt, challenge, proxyless=True, proxy_details=None):
Формирует JSON-пакет с типом задачи. Если не используется прокси, тип задачи — "GeeTestTaskProxyless
", иначе — "GeeTestTask
". В пакет включаются обязательные параметры: URL страницы,gt
иchallenge
. Дополнительно можно передать данные о прокси, если они указаны. После формирования запроса выполняется POST-запрос к API 2captcha для создания задачи, и возвращается ответ в формате JSON.create_geetest_v4_task(website_url, captcha_id, proxyless=True, proxy_details=None):
Аналогичным образом формируется задача для GeeTest V4. Отличительной особенностью является указание версии (число 4) и вложенного словаряinitParameters
, содержащегоcaptcha_id
.
Функция опроса результата (get_task_result):
Функция отправляет периодические POST-запросы к 2captcha API (на адрес GET_TASK_RESULT_URL
), передавая идентификатор задачи (task_id
) и API-ключ.
Если в ответе статус равен "
processing
", функция выводит сообщение о том, что капча ещё не решена, и ждёт указанное время (по умолчанию 5 секунд) перед следующим запросом.Если получен иной статус (например, готовое решение), возвращается результат.
После исчерпания максимального количества попыток возвращается сообщение об ошибке (timeout).
Основная функция main:
Парсинг аргументов командной строки:
Используется модульargparse
для обработки обязательного параметра--website-url
(URL страницы с капчей) и опциональных параметров для настройки прокси (--proxy-type
,--proxy-address
,--proxy-port
,--proxy-login
,--proxy-password
).Настройка работы с прокси:
Если указаны параметры прокси, переменная proxyless устанавливается в False и формируется словарьproxy_details
с соответствующими данными. Если прокси не используются,proxyless
остаётсяTrue
.Извлечение параметров капчи:
Выводится сообщение о загрузке страницы, затем вызывается функцияauto_extract_params
, которая возвращает:Объект драйвера Selenium (для управления браузером).
Версию капчи ("3" или "4").
Для V3 – значения
gt
иchallenge
, для V4 – значениеcaptcha_id
.
Если драйвер или версия не получены, выводится сообщение об ошибке и выполнение прерывается.
Создание задачи на решение капчи:
В зависимости от версии:Для GeeTest V3:
Проверяется наличие значенийgt
иchallenge
. Затем выводятся параметры, и вызывается функцияcreate_geetest_v3_task
.Для GeeTest V4:
Проверяется наличиеcaptcha_id
. Выводится значение и вызывается функцияcreate_geetest_v4_task
.Если версия не распознана, скрипт завершает работу.
Обработка ответа от 2captcha:
Если API возвращает ошибку (проверка errorId), выводится сообщение об ошибке, браузер закрывается, и выполнение завершается.
Если задача успешно создана, выводитсяtask_id
, и начинается ожидание решения капчи через функциюget_task_result
.Получение и вывод решения капчи:
После успешного получения результата (решение капчи) оно выводится в формате отформатированного JSON.Внедрение решения в страницу через JavaScript:
С помощью метода driver.execute_script
в зависимости от версии выполняется:Для GeeTest V3:
Создаются (или обновляются) скрытые поля формы с идентификаторами geetest_challenge,
geetest_validate
иgeetest_seccode
с соответствующими значениями из решения.Обновляется содержимое элемента с идентификатором
#embed-captcha
, выводя сообщение об успешном прохождении капчи с отображением параметров.
Для GeeTest V4:
Простой скрипт заменяет содержимое элемента #embed-captcha сообщением об успешном прохождении капчи.
Задержка и завершение работы браузера:
После внедрения решения в страницу скрипт ожидает 30 секунд (для визуальной проверки результата) и затем закрывает браузер.
В скором времени хочу еще потестить SolveCaptcha - посмотреть как они справляются с распознаванием.
Таким образом, скрипт выполнен так, чтобы можно было визуально увидеть, что капча решена. Ниже привожу запись экрана, как у меня отработал этот скрипт.
Заключение
В данной статье я подробно рассмотрел принципы работы Geetest CAPTCHA и даже попытался показать, что обойти ее можно, даже при наличии минимальных навыков в программировании (Python может считаться программированием?).
Но нужно быть максимально внимательным в извлечении всех необходимых параметров, так как можно провозиться не один час с каким нибудь динамичным Challenge, как это произошло у меня.