Загрузка 0
ПОДЕЛИТЬСЯ

Мой блог

Листай вниз

Как я написал бота для автооткликов на hh.ru, который обходит платные подписки

Как я написал бота для автооткликов на hh.ru, который обходит платные подписки

Подробности изложены в материале первоисточника. Современный поиск работы часто упирается не в прохождение интервью, а в банальное получение приглашения. Отправляя резюме вручную, соискатели регулярно сталкиваются с глухой тишиной или автоматическими отказами. Популярные платформы вроде HeadHunter оперативно монетизируют эту боль через подписки вроде hh PRO, предлагая за деньги ИИ-сопроводительные письма, поднятие анкеты в выдаче и расширенную статистику. Платить за базовую автоматизацию мне показалось избыточным, поэтому я разработал собственного бота, который берет на себя поиск вакансий, составление писем, ответы на каверзные вопросы опросников, анализ отказов и регулярный подъем резюме.

Запуск прошел бодро: логи радовали зелеными отметками [OK], однако реальность оказалась суровее. Открыв переписки с нанимателями, я обнаружил, что кнопка напоминания вовсе не нажималась, хотя программа уверяла в обратном. Навыки «добавлялись» в профиль призрачной кнопкой, показатель успешности резюме держался на фиктивных константах из кода, а львиную долю причин отказов искусственный интеллект просто выдумывал. В этом материале я подробно разбираю архитектуру инструмента, подводные камни автоматизации и то, как мне пришлось переписать логику каждого модуля.

Интерфейс поиска вакансий на платформе
Страница поисковой выдачи вакансий на платформе HeadHunter, с которой взаимодействует скрипт автоматизации.
Настройка конфигурации скрипта автооткликов
Файл настроек приложения, содержащий параметры подключения к браузерному профилю и параметры задержек.
Просмотр активных чатов с работодателями
Раздел мессенджера платформы, где бот ведет автоматический диалог с ИИ-ассистентами нанимателей.
Структура репозитория проекта на GitHub
Исходный код утилиты для автоматизации откликов, доступный в публичном репозитории автора.

Сравнение возможностей: платная подписка hh PRO против бесплатного бота

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

Реклама

Что получает владелец подписки и пользователи скрипта

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

  • Сопроводительные тексты: вместо платных лимитов задействуются бесплатные квоты Gemini и Groq, а также локальные или сторонние модели вроде Claude и ChatGPT при их наличии. Если лимиты исчерпываются, система переключается на продвинутые шаблоны.
  • Поднятие профиля: официальный функционал срабатывает фиксированное число раз, тогда как бот отслеживает таймер и нажимает заветную кнопку каждые четыре часа строго по расписанию.
  • Отклики: вместо ручного труда программа отправляет анкеты до двухсот раз в день, упираясь исключительно в суточные лимиты самой площадки.
  • Анкеты и опросники: сложные формы с десятками вопросов заполняются искусственным интеллектом на основе заданных в конфигурации неизменяемых параметров вроде отношения к полиграфу или требований по оформлению.
  • Анализ отказов: скрипт скачивает данные из архивов и мессенджеров, формируя честную статистику без придуманных платформой или кодом метрик.

Конечно, визуальные подсветки профиля, аналитика по конкурентам и функция «Хочу здесь работать» остаются эксклюзивом площадки. Но если эти маркетинговые бантики вам не важны, остальной функционал можно закрыть самостоятельно.

Почему я выбрал браузерную автоматизацию вместо официального API

Первым делом любой разработчик пытается задействовать официальный программный интерфейс. Однако стандартный метод POST /negotiations для сторонних приложений заблокирован и возвращает ошибку 403 Forbidden, а получить собственные резюме через легальные шлюзы проблематично. Некоторые утилиты обходят это ограничение, воруя ключи авторизации из официального мобильного приложения под Android. Я осознанно отказался от такого подхода, поскольку чужие токены несут риск мгновенной блокировки личного аккаунта.

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

В итоге была выстроена гибридная архитектура. Поиск вакансий частично идет через API там, где это разрешено, но основные данные собираются через стандартную поисковую выдачу сайта, поскольку штатные методы отдают далеко не всю базу. К примеру, первый же парсинг страницы выдал более восьми сотен позиций, отсутствовавших в кеше API. Состояние интерфейса поиска считывается из специального тега шаблона, где зафиксированы даже флаги наличия дополнительных анкет.

Реклама

В качестве движка автоматизации задействован Selenium, использующий мой собственный профиль браузера Chrome. Достаточно один раз пройти авторизацию руками, после чего сессия сохраняется. Вся история и отправленные данные фиксируются в локальной базе SQLite. При необходимости можно запускать несколько изолированных сессий в Docker-контейнере, управляя ими через удаленный интерфейс noVNC. Один типичный цикл работы включает проверку новых отказов, обновление резюме по таймеру и массовую отправку откликов до достижения суточного потолка.

Скорость работы и оптимизация задержек

После первичного запуска меня закономерно волновал вопрос производительности. Анализ логов показал медианное время обработки одной вакансии в пределах полутора минут, где львиную долю времени занимает генерация текста языковой моделью и отрисовка элементов интерфейса на тяжелых React-компонентах.

Попытки форсировать события не имеют особого смысла. Текст пишется моделями посимвольно, на что уходит несколько секунд. Пытаться вставлять сгенерированные строки через JavaScript напрямую бесполезно: сложный интерфейс площадки не фиксирует такие изменения, из-за чего отклики уходят с пустыми полями. Заложенная антибан-пауза в пару секунд между действиями идеальна, а дальнейшее ускорение упрется в жесткие лимиты самой платформы.

Проблемы с кликами в React-интерфейсе и ложные срабатывания логов

Интерфейс платформы построен на компонентной базе Magritte. Стандартные методы клика из Selenium вроде выполнения скриптов на чистом JavaScript в большинстве случаев молча проваливаются: ошибки не возникает, но событие просто теряется интерфейсом. Прямой вызов метода клика элемента часто приводит к исключениям из-за перекрывающих невидимых оверлеев.

Единственным надежным инструментом оказались цепочки действий ActionChains с предварительным наведением курсора. Тесты на моем аккаунте показали удручающую статистику: при выборе навыков из выпадающих списков простой JavaScript-клик срабатывал в нуль случаев из трех, тогда как инструмент позиционирования курсора отрабатывал безупречно. Аналогичная беда наблюдалась с кнопками мессенджера и выпадающими списками.

Почему ложные отчеты [OK] опасны для стабильности бота

Самое неприятное крылось не в самих кликах, а в том, как код реагировал на их выполнение. Раньше программа выполняла команду, моментально выводила в журнал победную строку [OK] и возвращала положительный результат, не утруждая себя проверкой изменений на экране. Я обнаружил это на примере кнопки напоминания об отклике и при автоматическом добавлении навыков.

Теперь правило во всей кодовой базе единое: после любого действия страница должна быть перечитана, а изменения — зафиксированы визуально. Если целевой элемент остался на прежнем месте, скрипт фиксирует предупреждение уровня WARNING, а не рапортует об успехе. Это избавило систему от иллюзии бурной деятельности.

Сброс навыков из-за скрытых лимитов интерфейса

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

Реклама

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

Логика сравнения текста и опасные ошибки оператора в опросниках

Повсеместное использование простого оператора вхождения подстроки приводило к казусам. Поисковые паттерны вроде проверки слова «готов» цепляли варианты «не готов», отправляя нанимателям противоположные ответы. Поиск ключевых слов в анкетах приводил к случайной установке галочек согласия на передачу данных сторонним партнерам или путал технический навык с посторонними терминами.

Особенно опасными были опросники о судимости или прохождении полиграфа. Ранее при отсутствии точного совпадения скрипт выбирал первый попавшийся вариант или среднее значение, из-за чего на деликатные вопросы система могла ответить утвердительно. Теперь логика поиска вариантов разделена на два строгих прохода: сначала ищется буквальное совпадение по всем меткам, а затем проверяются границы слов с обязательным исключением отрицательных конструкций. Если уверенного ответа нет, рискованная вакансия просто пропускается.

Аналитика отказов, которую искусственный интеллект придумывал сам

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

Логирование работы конкретной языковой модели
Обновленный формат системных логов, в котором четко указано, какая именно модель генерирует сопроводительное письмо.

Из сотен записей реальные текстовые причины от работодателей содержались лишь в небольшой части строк. Остальной массив заполнялся стандартными шаблонными фразами, которые бот подставлял самостоятельно в моменты, когда языковая модель не успевала ответить или возвращала ошибку. Настоящие претензии рекрутеров чаще всего сводились к банальному отсутствию сопроводительного письма или его шаблонности. После редизайна модуля вся сомнительная аналитика была помечена как предположения, а проценты стали считаться исключительно по подтвержденным данным.

Борьба с чужими подписями и системными строками в переписках

Аналогичные проблемы обнаружились при парсинге истории сообщений. В поле сопроводительного письма периодически подтягивались подписи самих рекрутеров, служебные уведомления платформы о подключении ИИ-ассистентов или контактные данные. После ужесточения фильтров валидации алгоритм стал безошибко отсекать мусорные системные строки, оставляя для анализа только подлинные реплики кандидата.

Надежная стратегия бесплатного использования языковых моделей

Финансовые затраты на стороннее API в мои планы не входили, поэтому была выстроена многоуровневая резервная очередь. Первыми в цепочке отрабатывают бесплатные модели Gemini и Groq, за ними следуют локальные инструменты продвинутых подписок, а в самом конце подключаются интеллектуальные шаблоны со спинтаксисом для генерации вариативных текстов.

Бесплатные лимиты Gemini требуют аккуратного обращения. Превышение суточных или минутных квот приводит к ошибкам 429, способным надолго парализовать работу скрипта. Раньше бот бездумно ожидал снятия блокировок, тратя часы на пустые паузы. Теперь при получении ошибки ограничения провайдера программа считывает рекомендованное время ожидания и мгновенно переключается на генерацию писем по качественным шаблонам, возвращаясь к нейросетям только после восстановления квот.

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

Автоматизация прохождения сложных анкет и беседа с чат-ботами рекрутеров

Пройти стандартный отклик — полдела. Многие вакансии встречают соискателей длинными опросниками, содержащими до пятидесяти пунктов. Отправлять вопросы по отдельности нельзя из-за ограничений квот, поэтому вся анкета уходит единым пакетом, а модель должна вернуть ответы строго в рамках заданных вариантов. Если длины массивов не совпадают, пакет забраковывается целиком, чтобы избежать смещения ответов.

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

Мелочи реализации, съевшие больше всего времени

На отладку неочевидных багов ушло немало вечеров. Вот лишь несколько ярких примеров:

  • Архивные карточки: алгоритм прокрутки страницы цеплял рекламные блоки снизу, где встречалась фраза «Вакансия в архиве», ошибочно отменяя отклики на актуальные предложения. Проверку пришлось строго привязать к структуре активных элементов.
  • Скрытые блокировки резюме: платформа иногда временно ограничивает видимость профиля сразу после ручного обновления, выдавая требование подтвердить видимость. Бот научился распознавать это состояние, делать паузу и возобновлять отправку позже.
  • Языковые локали: случайное переключение интерфейса площадки на английский язык ломало русскоязычные селекторы. Теперь скрипт при старте принудительно проверяет атрибуты документа и возвращает русскую локаль.

Созданная система представляет собой мощный инструмент автоматизации, но требует понимания рисков. Любые массовые действия в серой зоне правил требуют осторожности. Исходный код проекта доступен в публичном репозитории на GitHub, а для его запуска достаточно базовых навыков работы с терминалом и интерпретатором Python.

Реклама
01.