С инженерной точки зрения поиск работы — это процесс с низкой энтропией. Есть входящий поток данных (JSON с вакансиями) и есть необходимость отправить ответный сигнал (POST-запрос с откликом). Задача кажется тривиальной для автоматизации: написал парсер, настроил cron, пошел пить кофе.

Однако, если вы попробуете автоматизировать отклики на крупных job-board платформах (особенно на hh.ru) в 2026 году, вы столкнетесь с серьезным противодействием. WAF (Web Application Firewall), анализ TLS-отпечатков, поведенческая биометрия и теневые баны — это реальность, которая убивает скрипты на requests за пару часов.

В этой статье разберем архитектуру решения, которое позволяет автоматизировать процесс отклика, используя подходы RPA (Robotic Process Automation), мимикрию под поведение пользователя (Human Mimicry) и LLM для обхода смысловых фильтров.

(Дисклеймер: Статья носит исследовательский характер. Мы не призываем нарушать правила площадок, а разбираем технические методы эмуляции браузера).

Проблема 1: Почему requests больше не работает

Эпоха, когда можно было притвориться браузером, просто подставив User-Agent в заголовки, прошла лет 5 назад.

Современные системы защиты (Akamai, Cloudflare, Qrator и самописные решения площадок) анализируют TLS Fingerprint (JA3/JA3S). Стандартный клиент Python (будь то requests, aiohttp или httpx) имеет характерный порядок шифров при SSL-рукопожатии, который кардинально отличается от реального Chrome или Safari.

Симптомы:

  • Вы получаете 403 Forbidden на первый же запрос.

  • Вам отдают страницу с бесконечной капчей.

Можно использовать библиотеки с подменой TLS (например, curl_cffi), но это решает проблему только для парсинга (GET-запросы). Для совершения отклика (нажатие кнопок, ввод текста, работа с динамическим JS) необходим полноценный браузер.

Проблема 2: Headless Browser и его детекты

Мы переходим к Selenium или Playwright. Запускаем браузер в режиме headless=True (без графического интерфейса), чтобы экономить ресурсы сервера.

И тут нас ловит защита на клиенте (в JS).

Существует сотни признаков, выдающих бота:

  1. navigator.webdriver = true — классика.

  2. WebGL Fingerprint: У серверных видеокарт (которые рендерят страницу в headless) вендоры — это Google SwiftShader или VMware, а не NVIDIA или Intel Iris.

  3. Отсутствие плагинов: У живого человека всегда есть какой-то мусор в объекте navigator.plugins.

  4. Размер окна: Дефолтный 800x600 сразу выдает бота.

Решение: Патчинг на уровне браузера

Использование stealth-plugin для Puppeteer/Playwright помогает, но ненадолго.

Для стабильной работы мы используем кастомные сборки Chromium, где на уровне исходного кода вырезаны флаги автоматизации. Плюс — жесткая привязка профиля:

  • Каждому воркеру выдается уникальный «цифровой слепок» (Fingerprint).

  • Используются резидентские прокси (Datacenter IP моментально флагаются).

Проблема 3: Поведенческая биометрия (Behavioral Analysis)

Допустим, мы обошли технические проверки. Браузер выглядит как настоящий. Мы находим кнопку «Откликнуться» и кликаем.

Бан.

Почему? Потому что скрипт наводит курсор мгновенно. Или по идеальной прямой линии.

Современные скрипты антифрода пишут трек движения мыши. Человеческая рука имеет тремор, инерцию, разгон и торможение.

Реализация Human Mimicry (Кривые Безье)

Чтобы обойти это, мы написали модуль движения курсора, основанный на кубических кривых Безье.

Псевдокод логики движения:

Python

import numpy as np

def generate_human_path(start_x, start_y, end_x, end_y):
    # Добавляем случайные контрольные точки для отклонения от прямой
    ctrl_1_x = start_x + np.random.randint(-50, 50)
    ctrl_1_y = start_y + np.random.randint(-50, 50)
    
    # Генерируем точки кривой Безье
    t = np.linspace(0, 1, num=50) # 50 шагов анимации
    path_x = (1-t)**3 * start_x + 3*(1-t)**2 * t * ctrl_1_x ... 
    
    # Важно: добавить "шум" (микро-дрожание)
    noise = np.random.normal(0, 1.5, size=len(path_x))
    return path_x + noise, path_y + noise

Кроме того, бот никогда не жмет кнопку сразу. Он:

  1. Скроллит страницу вниз (эмуляция чтения).

  2. Делает паузу (Random Sleep от 3 до 12 секунд).

  3. Выделяет текст (иногда).

  4. Только потом кликает.

Архитектура системы

Чтобы масштабировать это решение (один браузер потребляет до 1 ГБ RAM), мы пришли к следующей архитектуре:

  • Core (Python/FastAPI): API-шлюз и база данных (PostgreSQL).

  • Orchestrator: Очередь задач (Redis). Контролирует лимиты. Важно: мы не разрешаем отправлять более 20 откликов в сутки с одного аккаунта, чтобы не триггерить поведенческие аномалии на бэкенде платформы.

  • Browser Nodes: Docker-контейнеры с Playwright. Поднимаются on-demand, отрабатывают сессию и умирают, очищая память.

  • LLM Module: Self-hosted Llama (или API OpenAI) для генерации текста.

Зачем здесь LLM?

  1. Парсинг верстки: CSS-селекторы меняются. Мы скармливаем LLM кусок DOM-дерева и просим вернуть JSON с описанием вакансии. Это устойчивее к редизайну сайта.

  2. Генерация Cover Letter: Шаблонные письма («Прошу рассмотреть...») имеют низкую конверсию. Модель читает вакансию и пишет ответ, подсвечивая релевантный опыт из резюме.

Автоматизация карьерной рутины — это классическая задача "Red Team vs Blue Team". На каждое действие (улучшение скрипта) платформа отвечает противодействием (новые метрики детекта).

В 2026 году написать простой скрипт для поиска работы уже невозможно. Порог входа вырос до уровня разработки полноценной RPA-системы с элементами AI. Стоит ли оно того? Учитывая, что это экономит десятки часов на механический скроллинг ленты — определенно да.

P.S. Где посмотреть тесты?

Мы проводим открытое тестирование системы. Публикуем логи обхода защит, сравниваем конверсию «ручного» поиска против RPA и делимся статистикой по офферам в разных грейдах.

Кому интересна техническая сторона вопроса или хочется протестировать агента на своем профиле — welcome в наш канал: [Ссылка на ТГ-канал]

Комментарии (14)


  1. ingeniare
    07.01.2026 06:01

    Все круто конечно, но у HH есть API. Я ещё год назад написал автоотзовик на HH, когда искал работу (так и не нашёл кстати) - он сам искал релевантную вакансию по моему резюме и отзывался. Ну а я потом смотрел отказы, от ещё более тупых ботов с той стороны. HH площадка ботов.


    1. somech
      07.01.2026 06:01

      API для соискателя прикрыли


  1. nikulin_krd
    07.01.2026 06:01

    Зачем здесь LLM?

    Внятного ответа на этот вопрос мы так и не получили.

    Вместо того, чтобы пользоваться стандартным функционалом playwright - локаторы, вы на кой-то черт впихнули туда LLM….

    Про остальное я вообще молчу.


    1. akakoychenko
      07.01.2026 06:01

      Ага. Странный ход. Логично, что сайты, написанные белками-истеричками, намеренно говнят DOM (например, хз, как сейчас, но 5 лет назад инстаграм выводил текст кусочками по 1й-2м буквам в элементе), есть кейсы, когда интернет магазины специально цену от балды пишут в элементе, перекрытом другим, и так далее. И логично то, что единственный надёжный способ тут это OCR. В теории, если верстку пересобирают, условно, раз в сутки, то, возможно, самым производительным вариантом будет при помощи LLM писать высокопроизводительный парсер, давая DOM + результат OCR, чтобы LLM автоматически писал код, либо конфигурацию парсера, после каждого изменения на стороне НН.

      Но вот каждый раз дергать LLM на каждом цикле выглядит и ненадежным (ибо, почему нейронка должна адекватно спарсить намеренно запутанный DOM?), и более затратным, чем OCR, которые сегодня реально быстрые и эффективные


      1. Ilya_JOATMON
        07.01.2026 06:01

        Кстати насчет OCR. Можете что посоветовать, когда сайты с текстами тоже "шифруются" и напрямую текст не вытащить ни копипастой ни залезанием в http. Пробовал сделать скриндамп плагином хрома, это работает, но вот найти OCR которая дальше эту картинку распознает, с этим возникла проблема. Файнридеры отказались - кроме 9й, но там проблема с большими дампами - оно память выделить не может. gImageReader - тоже страдает от размеров дампа и распознает "не очень".


        1. Ravius
          07.01.2026 06:01

          Картинку режьте на стандартные кусочки и все.


          1. Ilya_JOATMON
            07.01.2026 06:01

            Разрезание может и текст разрезать.


        1. akakoychenko
          07.01.2026 06:01

          Из классики, гляньте на EasyOCR, paddlepaddle

          Из интересного нового, microsoft omniparser. Хотя, слишком тяжеловесное решение

          Ну, или, если задача не требует эффективности, любая state-of-the-art LLM неплохо OCRит (но, тут, понятно, стоимость будет неадекватной)


      1. nikulin_krd
        07.01.2026 06:01

        Если изучить как работают локаторы в playwright, то не нужны будут ни LLM, ни OCR))


  1. AlexeyK77
    07.01.2026 06:01

    Вместо тега "информационная безопасность" более подходящим будет тег "фрод".


    1. tarantula58910
      07.01.2026 06:01

      вы сотрудник, акционер или бенефициар ХХ ?
      если нет, нет и нет, то зачем ущемляетесь на пустом месте ?

      upd: фрод это другое. например разместить вакансию "тайный покупатель", а вербовать дропов,
      которые под предлогом проверки качества обслуживания будут получать пластик на свои документы и
      отдавать его кому скажут. или - заполнить анкету от "работодателя" на стороннем ресурсе и отослать ее якобы через телеграм, но этот "телеграм" ваши креды украдет а аккаунт угонит.


      1. AlexeyK77
        07.01.2026 06:01

        не имею отношения к вышеупомянутым организациям, но замечу, что название статьи говорит само за себя "RPA против Anti-Fraud".


  1. exelens
    07.01.2026 06:01

    А не проще ли сделать расширение для браузера, и пусть оно во вкладке отрабатывает?


  1. manwithbrain
    07.01.2026 06:01

    А можно подробнее про кастомные сборки Chromium? Где их брать?