Бот автоотклика на вакансии hh.ru: ищет по заданным запросам, оценивает релевантность по ключевым словам, отправляет отклик с сопроводительным письмом и ведёт историю, чтобы не откликаться дважды.
Задача была не «откликнуться везде», а отсеять нерелевантное и не сжечь аккаунт: отсюда порог по скорингу, дневной лимит, случайные задержки и безусловная остановка на капче.
- Автоматизация откликов может нарушать пользовательское соглашение hh.ru. Оцените риск для своего аккаунта сами. Бот не обходит защиту: капчу он не решает, а останавливается и зовёт человека.
- Дефолтный режим —
--dry-run. Реальная отправка только по явному--run. - Пароль нигде не хранится. Вход ручной, через видимое окно браузера; сохраняется только сессия Playwright, локально.
- Файл сессии
storage_state.json= доступ к аккаунту без пароля. Он в.gitignore. Не коммитьте его и не пересылайте.
поиск по keywords → скоринг → фильтр по threshold → проверка истории
↓
лимит на день ← задержка ← отправка отклика с письмом
Скоринг — без LLM, намеренно. Считаются совпадения по двум спискам:
role_keywords (кем зовут) весит больше, stack_keywords (с чем работать) —
меньше. Взвешенная сумма делится на normalizer и обрезается до 1.0.
Слово из exclude_keywords в заголовке обнуляет вакансию сразу.
Для отбора «релевантно / нет» этого хватает, а десятки вакансий в день через LLM — это лишние деньги, задержка и недетерминированность там, где она не нужна.
Письмо — один шаблон с подстановкой {vacancy_title} и {company_name}.
Под каждую вакансию заново не генерируется, по той же причине.
История — sqlite. Вакансия, на которую уже откликались, второй раз не берётся, даже если снова попала в выдачу.
pip install -r requirements.txtplaywright install chromiumПисьмо:
copy cover_letter_template.example.txt cover_letter_template.txtОткройте cover_letter_template.txt и заполните своими данными. Инструкция —
внутри файла, комментарии оттуда нужно удалить.
Всё в config.yaml, код трогать не нужно. Основное:
| Параметр | Что задаёт |
|---|---|
search.keywords |
Поисковые запросы; каждый гоняется отдельно, результаты объединяются |
search.area |
ID региона hh.ru (1 — Москва) |
search.experience |
noExperience / between1And3 / between3And6 / moreThan6 |
scoring.role_keywords |
Совпадения по должности, вес role_weight |
scoring.stack_keywords |
Совпадения по стеку, вес stack_weight |
scoring.exclude_keywords |
Слово в заголовке → вакансия отбрасывается |
scoring.threshold |
Порог отклика, 0..1 |
limits.daily_applications |
Жёсткий потолок откликов в сутки |
limits.delay_*_seconds |
Диапазон случайной паузы между действиями |
browser.headless |
false — видимое окно, для отладки селекторов |
config.smoketest.yaml — урезанный конфиг для быстрой проверки, что всё
вообще работает.
Один раз — ручной вход. Откроется окно браузера, войдите сами, пройдите 2FA:
python main.py --loginПосмотреть, кому бот откликнулся бы, ничего не отправляя:
python main.py --dry-runПрогоните dry-run и прочитайте список глазами. Только потом:
python main.py --runЧитайте этот раздел как часть документации, а не как формальность.
- Селекторы hh.ru не проверены на живом прогоне после последних правок.
Вёрстка сайта меняется, и при расхождении бот молча не найдёт кнопку.
Первый запуск делайте с
browser.headless: falseи смотрите на экран. - Скоринг по подстрокам не понимает контекста: «не требуется Python» засчитается как совпадение по стеку.
- Вакансии с обязательным сопроводительным вопросом или внешней формой отклика не обрабатываются.
- Тестов нет. CI нет.
- Проверялось только на hh.ru и только на русскоязычной выдаче.
storage_state.json (сессия), applications.db и applications_export.csv
(история откликов), заполненный cover_letter_template.txt (контакты) —
всё это создаётся локально при первом запуске и остаётся на вашей машине.
Лицензия не выбрана — по умолчанию все права сохранены за автором.