Когда работаешь с автоматизацией, стандартных инструментов вроде антидетект-браузера и прокси недостаточно. Автоматизация — это не просто про изолированный профиль с уникальным прокси, это еще и про понятное и предсказуемое поведение, вернее, не предсказуемое, а человечное. Это раньше скрипты могли действовать максимально топорно и в лоб, сейчас такое поведение очень быстро стриггерит алгоритмы защиты сайта.

Системы защиты давно научились определять человека (ну или по крайней мере предполагать, что имеют дело с человеком) по паттернам поведения. Они (скрипты антифрода) умеют записывать каждое движение мыши на странице. И если скрипт двигает курсор как машина (у него идеальная прямая, постоянная скорость и клики всегда попадают строго в центральный пиксель кнопки) — для защитных алгоритмов это мгновенный сигнал тревоги.

Привет! Я Александр, пишу для антидетект-браузера Aurorium и по совместительству являюсь специалистом в вопросах автоматизации и парсинга. И так как я обозвал себя специалистом по автоматизации, кое-какую информацию мне удалось накопить за то время, когда я воевал с защитными алгоритмами. По сложившейся уже традиции сварите чашечку кофе или чая (постоянные читатели уже должны были автоматизировать этот процесс), сэмулируйте пару бутербродов (но лучше все же сделайте реальные) — мы начинаем!
Давайте разберемся, на что именно смотрит антифрод, почему обычные рандомные действия могут сделать только хуже, и как заставить курсор двигаться максимально естественно.
Как именно антифрод следит за вашим курсором
Думать, что антифрод-скрипты работают по каким-то секретным алгоритмам неправильно: в основе их работы с поведенческими паттернами кроется банальный сбор телеметрии.
Как только страница загружается в браузере, защитный скрипт вешает глобальные слушатели на стандартные события мыши: mousemove, pointermove, mousedown и mouseup. С этого момента каждое микродвижение записывается в буфер и отправляется на сервер для дальнейшего анализа.
Для каждого шага мыши антифрод сохраняет простую цепочку данных: координата X, координата Y и точное время в миллисекундах с помощью таймера performance.now().
В итоге сайт получает не обычные координаты финального клика, а полноценный таймлайн вашего поведения:
[Точка 1: x=120, y=340, время=10.2 мс]
-> [Точка 2: x=125, y=342, время=18.4 мс]
-> [Точка 3: x=134, y=346, время=26.1 мс] ...Как я уже написал, все эти точки отправляют на сервер, где проводится их анализ:
Профиль скорости: Скрипт защиты делит пройденное расстояние между соседними точками на прошедшее время. Так он строит график вашей скорости. У живого человека этот график будет дугообразным: резкий старт, разгон в середине пути и плавное торможение перед кнопкой.
Анализ рывков и ускорения: Если курсор прыгает с нулевой скорости на скорость 1000 пикселей в секунду без промежуточного разгона, ускорение в точке старта стремится к бесконечности. Это свидетельствует о ботоподобном поведении
Детектор углов и кривизны: Сенсор вычисляет угол поворота стрелки между каждыми тремя соседними точками. Ниже об этом расскажу подробнее.
Аппаратный пульс мыши: Физическая мышь подключена к компьютеру по USB и передает свои координаты пачками с фиксированной частотой опроса порта. Скрипты автоматизации часто выстреливают такие события вообще без задержек (с разницей в 0 миллисекунд) или ставят хаотичные паузы. Антифрод строит гистограмму интервалов между событиями, и если на ней нет четких интервалов, кратных реальному USB-порту, сайт понимает, что события генерирует виртуальный эмулятор.
Сессионная память (сравнение действий): Антифрод запоминает не одно движение, а сравнивает их серию внутри сессии. Если вы сделали на сайте 5 кликов, и все 5 перемещений заняли, условно, 200 миллисекунд или изогнулись по одинаковой дуге — система поймет, что перед ней бот.
Если интересно почитать про поведенческий антифрод более подробно, у нас есть отдельная статья, посвященная этому — Поведенческий антифрод.
Что выдает бота или Четыре главные ошибки скрипта
Перед тем как рассказывать, как надо делать, давайте покопаемся в исходниках и разберем, как не надо. Начать следует с простого, представьте, что вы взяли в руку мышку (для трушных IT-шников — положили руку на мышь сверху), вы думаете о координатах? Если в этот момент сын не принес задачку по геометрии — вряд ли. Вы просто смотрите на кнопку, двигаете рукой, а мозг постоянно подруливает вашу траекторию.
Классические боты (хочется написать из 90-х, но давайте ограничимся 2010–2020 годами) обычно ведут себя иначе:
Четкие движения. Курсор перемещается из точки А в точку Б по идеально прямой линии. В реальности же провести курсор строго по линейке через весь экран физически невозможно.
Скорость. Скрипт начинает движение с конкретной постоянной скоростью и с ней же прибывает к своему пункту назначения. Человек так не двигается. После того как мышь оказалась в руке, реальный пользователь хаотичными движениями руки пытается найти курсор на экране, а когда глаз зацепился за курсор, рука начинает разгоняться, летит к цели, а в конце плавно притормаживает, чтобы прицелиться точнее.
Всегда точно в цель. Скрипт всегда нажимает четко в ту точку, которая указана у него в настройках. Когда антифрод видит, что за N кликов координаты попадания не сместились даже на пару пикселей, он начинает что-то подозревать.
Высокий темп или телепортация. Бывает и такое, что курсор за 0 миллисекунд перескакивает через весь экран или генерирует сотни событий движения с интервалом в доли миллисекунды, это противоречит не то что физике обычных USB-мышек, это в принципе противоречит физическим законам.
Как это исправить
Чтобы поведенческие сенсоры перестали видеть в вашем скрипте бездушную машину и начали относиться к нему как вершине пищевой цепочки (по-простому — человеку), не нужно быть семи пядей во лбу или заканчивать Гарвард. Для начала будет достаточно внедрить четыре базовые привычки человеческой руки.
Плавная дуга вместо прямой (Кривые Безье)
Линия, по которой двигается ваш курсор, не должны быть идеально прямой, его путь должен быть слегка изогнутой дугой. Для этого используют кривые Безье.
Представьте, что мы натянули между курсором и кнопкой резинку. Если потянуть ее сбоку пальцем в одной или двух точках, резинка изогнется, вот именно этот эффект нам и нужен.
Вот как это работает в коде: берем начальную и конечную точку, а между ними случайным образом вставляем еще две невидимые опорные точки. Они (невидимые точки) будут слегка «оттягивать» траекторию в сторону. В итоге получается естественная мягкая дуга, форма которой меняется при каждом новом движении.

Разгон и притормаживание
Помимо кривой траектории, есть еще одно правило человеческой моторики (его называют законом Фиттса) — чем дальше кнопка и чем она меньше, тем больше времени требуется человеку, чтобы по ней попасть.
Еще нужно понимать, что движение делится на две фазы:
Бросок (разгон): первые полпути рука делает быстрый рывок вперед.
Доводка (торможение): когда курсор подлетает к кнопке, скорость падает, чтобы аккуратно навести стрелку на элемент.
Иногда рука по инерции проскакивает кнопку на пару пикселей дальше (овершут), после чего человек возвращает курсор назад. Если вы добавите такой вот мелкий промах хотя бы в 20% движений, доверие антифрода к скрипту станет выше.

Легкое дрожание руки
Не путаем похмельный тремор с микродрожанием руки. Оно есть у любого пользователя, даже у самого крутого дизайнера, который рисует всю графику исключительно вручную, оно тоже присутствует. Поэтому, когда вы пытаетесь оживить курсор кодом вроде координата + случайное_число антифрод может обнаружить синтетический шум. Ведь для генерации случайных чисел используют простой генератор, и это может спровоцировать резкие подергивания курсора в разные стороны.
Человеку свойственен микротремор мышц, рука как бы слегка «плавает» с очень маленькой амплитудой (в пределах одного-двух пикселей) и происходит это очень плавно.
Дрожание должно быть мягким и затухать в тот момент, когда курсор останавливается перед кликом.

Клик со смещением от центра
Люди редко попадают точно в центр кнопки. Обычно клики распределяются вокруг середины. Чаще ближе к центру, реже — ближе к краям, но практически никогда не вылетают за пределы элемента.
Достаточно добавить небольшое случайное смещение по горизонтали и вертикали, пропорциональное размеру кнопки, чтобы каждый новый клик имел уникальные координаты.

Почему обычные клики могут ломаться в коде
Для кого-то это покажется очевидным, однако, если вы пытаетесь нажимать на элементы через выполнение JavaScript на странице, у которой стоит защита, как например, код ниже:
document.querySelector('button').click();
Такое поведение будет промаркировано как скриптовое. В браузерах у каждого события есть скрытый флаг: event.isTrusted.
Если кнопку нажимает живой человек руками — флаг равен true.
Если клик вызван через скрипт element.click() или dispatchEvent — браузер поставит флаг false.
Антифрод считывает isTrusted в первую очередь. Очевидно, если там будет false, система даже не станет анализировать траекторию мыши, все будет понятно и без этого.
События нужно отправлять снаружи браузера, через системные команды отладки (в Playwright и Puppeteer это делает встроенный модуль page.mouse). Тогда браузер считает, что команду отдало настоящее физическое устройство.
Готовый скрипт человеческого движения на Python
Ниже — готовый и понятный пример функции для генерации человекоподобного наведения и клика. Код использует библиотеку Playwright, рассчитывает плавную траекторию, сбрасывает скорость перед кнопкой и делает органичный клик со смещением.
import math
import random
import asyncio
from playwright.async_api import async_playwright
def get_random_normal(mean=0.0, std_dev=1.0):
"""Генерация случайного числа с нормальным распределением (Бокс -- Мюллер)."""
u1 = max(1e-10, random.random())
u2 = random.random()
z0 = math.sqrt(-2.0 math.log(u1)) math.cos(2.0 math.pi u2)
return mean + z0 std_dev
def generate_curve_points(start_x, start_y, end_x, end_y, steps=25):
"""Строит кубическую кривую Безье со случайным натяжением и микротремором."""
dx = end_x - start_x
dy = end_y - start_y
distance = math.hypot(dx, dy)
if distance < 5:
return [(end_x, end_y)]
bend_direction = 1 if random.random() > 0.5 else -1
spread = distance random.uniform(0.15, 0.3) bend_direction
# Нормаль (перпендикулярный вектор)
normal_x = -dy / distance
normal_y = dx / distance
# Опорные точки кубической кривой
ctrl1_x = start_x + dx 0.3 + normal_x spread
ctrl1_y = start_y + dy 0.3 + normal_y spread
ctrl2_x = start_x + dx 0.7 + normal_x (spread 0.5)
ctrl2_y = start_y + dy 0.7 + normal_y (spread 0.5)
points = []
for step in range(steps + 1):
progress = step / steps
# Smoothstep (разгон и замедление)
eased_t = progress progress (3.0 - 2.0 progress)
# Точка на кубической кривой Безье
inv = 1.0 - eased_t
x = (inv**3 start_x +
3.0 inv**2 eased_t ctrl1_x +
3.0 inv eased_t**2 ctrl2_x +
eased_t*3 end_x)
y = (inv*3 start_y +
3.0 inv**2 eased_t ctrl1_y +
3.0 inv eased_t**2 ctrl2_y +
eased_t*3 end_y)
# Микротремор, затухающий к финишу
jitter = (1.0 - progress) 0.8
x += get_random_normal(0, jitter)
y += get_random_normal(0, jitter)
points.append((round(x, 1), round(y, 1)))
return points
async def human_click(page, selector: str):
"""Человекоподобное наведение и клик по элементу."""
element = await page.wait_for_selector(selector)
box = await element.bounding_box()
if not box:
raise ValueError("Элемент не найден или невидим на экране")
# 1. Смещенная точка клика (Гауссово распределение внутри габаритов)
center_x = box["x"] + box["width"] / 2.0
center_y = box["y"] + box["height"] / 2.0
offset_x = get_random_normal(0, box["width"] / 6.0)
offset_y = get_random_normal(0, box["height"] / 6.0)
target_x = max(box["x"] + 2, min(box["x"] + box["width"] - 2, center_x + offset_x))
target_y = max(box["y"] + 2, min(box["y"] + box["height"] - 2, center_y + offset_y))
# 2. Исходная позиция курсора
current_x = target_x - random.uniform(200, 450)
current_y = target_y + random.uniform(100, 300)
# Имитируем, что мышь уже где-то находилась на экране
await page.mouse.move(current_x, current_y)
await asyncio.sleep(random.uniform(0.1, 0.2))
# 3. Расчет траектории
distance = math.hypot(target_x - current_x, target_y - current_y)
total_steps = max(15, int(distance / 15))
path = generate_curve_points(current_x, current_y, target_x, target_y, steps=total_steps)
# 4. Движение курсора
for px, py in path:
await page.mouse.move(px, py)
await asyncio.sleep(random.uniform(0.007, 0.010))
# 5. Пауза перед кликом и само нажатие с удержанием
await asyncio.sleep(random.uniform(0.06, 0.12))
await page.mouse.down()
await asyncio.sleep(random.uniform(0.06, 0.09))
await page.mouse.up()О чем стоит помнить
Очеловечивание движений курсора — важная деталь, и она снимает подозрения систем поведенческой аналитики.
Однако важно понимать, какая бы безупречная мышь не была, она не спасет сессию, если сайт увидит грязный IP-адрес из черного списка, стандартный User-Agent от headless-браузера или несостыковки в отпечатках Canvas и WebGL.
Реалистичная механика мыши всегда должна работать в синергии с надежным изолированным профилем антидетект-браузера и чистыми прокси. Когда браузер выглядит как реальный компьютер, а курсор ведет себя как живая рука, автоматизация становится по-настоящему невидимой.

Комментарии 0