HH Applicant Tool
Прочтите обращение основателя Wikipedia разработчика
> [!IMPORTANT] > Прежде чем задавать долбоебские вопросы, прочитай весь текст ниже, особенно пункт [ответы на заебавшие вопросы](#ответы-на-заебавшие-вопросы).Авторские права на изображение принадлежат Кандинскому... нет, не человеку, а боту от говносбера
☕ Поддержать проект
BTC Address: BC1QWQXZX6D5Q0J5QVGH2VYXTFXX9Y6EPPGCW3REHS (спасибо за донаты — это единственное, что двигает разработку, так что не стесняйтесь и шлите их чаще, вы ЭКОНОМИТЕ КУЧУ ДЕНЕГ с учетом жадности владельцев сервисов для откликов)
Особенности и функционал
- 🐍 Утилита написана на Python, а значит может быть запущена на любой системе, куда его можно установить (даже на Android)
- 🤖 Поддержка ИИ (далее по тексту хуИИ)
- 📤 Рассылка откликов
- ✍️ Автоматическая генерация сопроводительных
- 🧩 Решение тестов при отклике и распознание капчи
- 💬 Автоответчик в чате с работодателем, в тч для общения с ботом рекрутером
- 📧 Дополнительная рассылка писем на Email работодателям
- 👥 Поддержка нескольких аккаунтов через профили
- 📇 Сохранение контактов работодателей и информации о вакансиях
- 🔒 Данные хранятся локально, никаких скрытых зондов и отправки данных на сторонние сервера, а значит ваши данные не утекут в результате взлома или не будут переданы различным мошенникам
- 📖 Открытый исходный код
- 🖥️ Может запускаться на сервере. В отличии от других реализаций не использует Headless Chromium, поэтому потребляет минимальное количество ресурсов, что критично для бюджетных серверов. Хромиум сильно грузит процессор, потребляет сотни мегабайт оперативной памяти при открытии hh и требует 1-2 гигабайт свободного места. Так что утилите хватит и VDS за пару сотен.
- 🎨 Наличие простого UI
- 🎁 И другой функционал абсолютно бесплатно!
Содержание
- HH Applicant Tool
- ☕ Поддержать проект
- Особенности и функционал
- Содержание
- Установка
- Авторизация
- Описание команд
- Капча
- Запуск через Docker
- Настройки и данные приложения
- Дополнительные настройки
- Шаблоны сообщений
- Интеграция и использование в скриптах
- Принцип работы
- Разработка
- Ответы на заебавшие вопросы
- Связанные проекты
- Лицензионное соглашение (Limited Non-Commercial License)
Установка
Утилиту можно установить из PyPi или скачать из репозитория. Из остальных мест ее скачивать не рекомендуется.
Установка:
# Стандартный pip (в Linux только в виртуальное окружение получится установить)
$ pip install hh-applicant-tool
# Чтобы утилита поддерживала команду ui
$ pip install 'hh-applicant-tool[ui]'
# Но лучше использовать pipx
$ pipx install hh-applicant-tool
# Последнюю версию можно установить через git
$ pipx install "git+https://github.com/s3rgeym/hh-applicant-tool"
# Для обновления до новой версии
$ pipx upgrade hh-applicant-tool
pipx добавляет исполняемый файл hh-applicant-tool в ~/.local/bin, делая эту команду доступной. Путь до ~/.local/bin должен быть в $PATH (в большинстве дистрибутивов он добавлен).
Использование виртуальных окружений в Linux/Mac:
mkdir -p ~/.venvs
python -m venv ~/.venvs/hh-applicant-tool
# Это придется делать постоянно, чтобы команда hh-applicant-tool стала доступна
. ~/.venvs/hh-applicant-tool/bin/activate
pip install hh-applicant-tool
Отдельно я распишу процесс установки в Windows в подробностях:
-
Для начала поставьте последнюю версию Python 3 любым удобным способом.
-
Запустите PowerShell (не
CMD.EXE, блять, а именно поверщель) от Администратора и выполните:Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
Данная политика разрешает текущему пользователю (от которого зашли) запускать скрипты. Без нее не будут работать виртуальные окружения.
Далее можно поставить pipx и вернуться к инструкции вверху раздела:
-
Всё так же от администратора выполните:
python -m pip install --user pipx
А затем:
python -m pipx ensurepath
-
Перезапускаем Terminal/Powershell и проверяем:
pipx -h
С использованием вирт. окружений:
-
Создайте и активируйте виртуальное окружение:
PS> python -m venv hh-applicant-venv PS> .\hh-applicant-venv\Scripts\activate
-
Поставьте все пакеты в виртуальное окружение
hh-applicant-venv:(hh-applicant-venv) PS> pip install hh-applicant-tool
-
Проверьте, работает ли оно:
(hh-applicant-venv) PS> hh-applicant-tool -h
-
В случае неудачи вернитесь к первому шагу.
-
Для последующих запусков сначала активируйте виртуальное окружение.
Авторизация
Авторизация по коду:
$ hh-applicant-tool authorize 79012345678
📱 Введите полученный код из SMS: 1488
🎉 Авторизация прошла успешно!
Проверка авторизации:
$ hh-applicant-tool whoami
🆔 27405918 Кузнецов Андрей Владимирович [ 📄 1 | 👁️ +115 | ✉️ +28 ]
В случае успешной авторизации токены будут сохранены в auth.json:
Токен доступа выдается на две недели. Для его обновления нужно выполнить:
hh-applicant-tool refresh-token
Он обновляется автоматически.
Помните, что у refresh_token тоже есть время жизни, поэтому может потребоваться полная авторизация, если долго не запускать утилиту + время жизни cookies может быть меньше времени жизни токенов, поэтому часть функционала, требующая авторизации на сайте может отваливаться, поэтому в любой непонятной ситуации следует попробовать авторизоваться по-новой.
Описание команд
Примеры команд:
# Общий вид: сначала глобальные настройки, затем команда и её аргументы
$ hh-applicant-tool [options] <operation> [args]
# Справка по глобальным флагам и список операций
$ hh-applicant-tool -h
# Справка по операции
$ hh-applicant-tool authorize -h
# Авторизуемся
$ hh-applicant-tool authorize
# Авторизация с использованием другого профиля
$ hh-applicant-tool --profile profile123 authorize
# Рассылаем заявки
$ hh-applicant-tool apply-vacancies
# Для тестирования поисковой строки и других параметров, используйте --dry-run.
# С ним отклики не отправляются, а лишь выводятся сообщения
$ hh-applicant-tool -vv apply-vacancies --search "Python программист" --per-page 3 --total-pages 1 --dry-run
# Фильтруем вакансии по исключаемым словам (fullstack, junior, php и т.п.)
$ hh-applicant-tool apply-vacancies --search "Python backend" --excluded-filter "fullstack,junior,php,java" --dry-run
# Поднимаем резюме
$ hh-applicant-tool update-resumes
# Ответить работодателям
$ hh-applicant-tool reply-employers
# Просмотр лога в реальном времени
$ hh-applicant-tool log -f
# Посмотреть содержимое конфига
$ hh-applicant-tool config
# Редактировать конфиг в стандартном редакторе
$ hh-applicant-tool config -e
# Вывести значение из конфига
$ hh-applicant-tool config -k token.access_token
# Установить значение в конфиге, например, socks-прокси
# Учтите, что при таком способе изменения конфига, исчезнут все комментарии в нем!!!
$ hh-applicant-tool config -s proxy_url socks5h://localhost:1080
# Удалить значение из конфига
# Эта операция тоже форматирует конфиг, удаляя из него комментарии
$ hh-applicant-tool config -u proxy_url
# Утилита все данные об откликах хранит в SQLite
$ hh-applicant-tool query 'select count(*) from vacancy_contacts;'
# Экспорт контактов в csv
$ hh-applicant-tool query 'select * from vacancy_contacts' --csv -o
contacts.csv
# Выполнение запросов в интерактивном режиме
$ hh-applicant-tool query
# Чистим отказы
$ hh-applicant-tool clear-negotiations
# При обновлении может сломаться схема БД, для ее починки нужно выполнить
# поочерёдно все миграции, добавленные после выхода последней версии
$ hh-applicant-tool migrate
List of migrations:
[1]: 2026-01-07
Choose migration [1] (Keep empty to exit): 1
✅ Success!
# Вывести все настройки
$ hh-applicant-tool settings
# Получить значение по ключу
$ hh-applicant-tool settings auth.username
# Установить email, используемый для автологина
$ hh-applicant-tool settings auth.username 'user@example.com'
Глобальные настройки:
-vиспользуется для вывода отладочной информации. Два таких флага, например, выводят запросы к API.-c <path>путь до каталога, где хранятся конфигурации.--profile <profile-id>можно указать профиль, данные которого будут храниться в подкаталоге.--use-kitty/--use-sixelиспользовать опр протокол для отображения капчи в терминале. Если не указывать, то капча будет отображена черезtkinter, если тот установлен....--use-ai/--aiиспользовать автоматизацию распознания капч и прочих действий через ИИ.
| Операция | Описание |
|---|---|
| authorize, auth | Авторизация на hh.ru. Введенные логин и пароль "запоминаются" и будут использованы при следующем вызове команды. |
| logout | Выйти из профиля (отозвать токен) |
| ui | Запуск UI для рассылки и управления откликами |
| whoami, id | Выводит информацию об авторизованном пользователе |
| list-resumes, list, ls | Список резюме |
| update-resumes, update | Обновить все резюме. Аналогично нажатию кнопки «Обновить дату». |
| clone-resume | Клонировать резюме |
| apply-vacancies, apply | Откликнуться на подходящие вакансии. Если указана строка для поиска (--search), то поиск будет произведен по вакансиям, иначе по списку рекомендованных вакансий. Лимит = 200 в день. На HH есть спам-фильтры, так что лучше не рассылайте отклики со ссылками, иначе рискуете попасть в теневой бан. |
| reply-employers, reply | Ответить во все чаты с работодателями, где нет ответа либо не прочитали ваш предыдущий ответ |
| autoresponder | ИИ-автоответчик, который отвечает в чатах с работодателями в тч на тесты (к удалению, так как некому его дописать) |
| clear-negotiations | Отмена откликов |
| clear-skipped | Очистка базы пропущенных вакансий. С флагом --reason можно указать конкретную причину (ai_rejected, excluded_filter). |
| call-api, api | Вызов произвольного метода API с выводом результата. |
| refresh-token, refresh | Обновляет access_token. |
| config | Показывает содержимое конфига. С флагом -e открывает его для редактирования. |
| settings | Просмотр и управление настройками в базе данных. Простое key-value хранилище. |
| install | Устанавливает зависимости, такие как браузер Chromium, необходимые для авторизации. |
| uninstall | Удаляет браузер Chromium, используемый для авторизации. |
| check-proxy | Проверяет используемые прокси |
| migrate | Починить базу |
| query | Выполнение SQL-запросов к базе. Схема БД находится в файле schema.sql. Если скормить ее DeepSeek, то он поможет написать любой запрос. |
| log | Просмотр файла-лога. С флагом -f будет следить за изменениями. В логах частично скрыты идентификаторы в целях безопасности. |
Утилита использует систему плагинов. Все они лежат в operations. Модули, расположенные там, автоматически добавляются как доступные команды. За основу для своего плагина можно взять whoami.py.
Для тестирования запросов к API используйте команду call-api совместно с jq для обработки JSON.
Пример поиска работодателей:
$ hh-applicant-tool call-api /employers text="IT" only_with_vacancies=true | jq -r '.items[].alternate_url'
https://hh.ru/employer/1966364
https://hh.ru/employer/4679771
...
Синтаксис call-api немного похож на httpie или curlie:
hh-applicant-tool call-api [{GET|POST|PUT|DELETE}] <endpoint> [<key=value> ...]
Если используется метод GET или DELETE (или ничего не указано), то параметры будут переданы как query string. Во всех остальных случаях параметры передаются как application/x-www-form-urlencoded в теле запроса.
Для поиска вакансий по региону в apply-vacancies нужно указать параметр area. Можно его указывать как для страны (каз - 40, бел - 2238), так и отдельного города (мск - 1).
Пример поиска area id по стране и названию:
$ hh-applicant-tool api /areas | jq -r '.[] | select(.name == "Беларусь") | .. | objects | select(.name == "Барановичи")'
{
"id": "2239",
"parent_id": "2233",
"name": "Барановичи",
"areas": [],
"utc_offset": "+03:00",
"lat": 53.16592,
"lng": 29.00806
}
Документация для работы с API соискателей была удалена с ха-ха.сру и его корпоративного репозитория. Можете не искать, они затерли даже историю репозитория. Но я через веб-архив выкачал документацию. Чтобы ее посмотреть, клонируйте этот репозиторий и откройте файл ./docs/hhapi/openapi.yml, например, с помощью Swagger Viewer.
Также существуют CLI-утилиты:
sudo pacman -S openapi-tui
openapi-tui --input docs/hhapi/openapi.yml
Для тех, кто любит просматривать документацию в браузере:
npx @redocly/cli preview -d docs/hhapi
Потом нужно открыть в браузере http://localhost:4000.
Капча
Если вызов какого-то метода API генерирует ошибку капчи, то она перехватывается, а далее происходит следующее:
- при флаге
--use-aiкапча будет распознаваться нейронкой - иначе:
- капча отображается в терминале, если есть флаги
--use-kitty/--use-sixelс именами используемых протоколов для отображения изображений (первый поддерживает даже Windows Terminal) - в противном случае капча будет отображена через всплывающее окно
tkinter
- капча отображается в терминале, если есть флаги
Некоторые модели плохо распознают русский язык, английский для можно установить с помощью передачи --captcha-lang EN.
Диалог для ввода капчи
Запуск через Docker
Это рекомендованный разработчиком способ. Если не работает стандартная установка, то используйте его. Также это самый простой способ запуска и использования утилиты, требующий скопипастить 5 команд.
Для начала нужно установить docker и docker-compose:
sudo apt install docker.io docker-compose-v2
Выкачиваем репозиторий и переходим в каталог:
git clone https://github.com/s3rgeym/hh-applicant-tool
cd hh-applicant-tool
Теперь авторизуемся:
# Чтобы ввести капчу в терминале укажите флаги --use-sixel/-s или --use-kitty/-k
# Это названия протоколов, которые исп-ся терминалом для отображения изображений
# Sixel поддерживается большинством терминалов
# А вот Windows Terminal поддерживает только Kitty
docker compose run -u docker -it hh_applicant_tool \
hh-applicant-tool -s -vv auth <username>
Если использовать флаг --ai, то капча будет разгадана нейронкой и останется только ввести код подтверждения из SMS/Email.
Подробно про авторизацию можно почитать здесь.
В случае успешной авторизации можно запускать рассылку откликов по крону:
docker compose up -d
Что будет делать?
- Рассылать отклики со всех опубликованных резюме.
- Поднимать резюме.
Просмотр логов cron:
docker compose logs -f
Чтобы прекратить просмотр логов, нажмите Ctrl-C.
Запущенные сервисы докер стартуют автоматически после перезагрузки. Остановить их можно выполнив:
docker compose down
Чтобы обновить утилиту, в большинстве случаев достаточно в каталоге выполнить:
git pull
В редких случаях нужно пересобрать все:
docker compose up -d --build
Чтобы рассылать отклики с нескольких аккаунтов, нужно переписать docker-compose.yml:
services:
# Не меняем ничего тут
hh_applicant_tool:
# ...
# Добавляем новые строки
# Просто копипастим, меняя имя сервиса, container_name и значение HH_PROFILE_ID
hh_second:
extends: hh_applicant_tool
container_name: hh_second
environment:
- HH_PROFILE_ID=second
hh_third:
extends: hh_applicant_tool
container_name: hh_third
environment:
- HH_PROFILE_ID=third
# Общий шаблон для новых профилей
уникальное_имя_сервиса:
extends: hh_applicant_tool
# может совпадать с именем сервиса
container_name: уникальное_имя_контейнера
environment:
- HH_PROFILE_ID=название_профиля
Обратите внимание на HH_PROFILE_ID — его значение указывается при авторизации, если профиль отличен от дефолтного. Далее нужно авторизоваться в каждом профиле:
# Авторизуемся со второго профиля
docker-compose exec -u docker -it hh_applicant_tool \
hh-applicant-tool --profile-id second -k auth
# Авторизуемся с третьего профиля
docker-compose exec -u docker -it hh_applicant_tool \
hh-applicant-tool --profile-id third -k auth
# И так далее
Ну и выполнить docker-compose up -d, чтобы запустить новые сервисы.
Команды можно потестировать в запущенном контейнере:
$ docker-compose exec -u docker -it hh_applicant_tool bash
docker@1897bdd7c80b:/app$ hh-applicant-tool config -p
/app/config/config.toml
docker@1897bdd7c80b:/app$ hh-applicant-tool refresh-token
ℹ️ Токен не истек, обновление не требуется.
docker@1897bdd7c80b:/app$
Если хотите команду apply-vacancies вызывать с какими-то аргументами, то создайте в корне файл apply-vacancies.sh:
#!/bin/bash
# Пример с фильтрацией по исключаемым словам
/usr/local/bin/python -m hh_applicant_tool apply-vacancies \
--excluded-filter "fullstack|junior|php" # укажите любые аргументы
В файлах startup.sh и crontab замените /usr/local/bin/python -m hh_applicant_tool apply-vacancies на /bin/sh /app/apply-vacancies.sh.
Настройки и данные приложения
Настройки и данные приложения хранятся по следующим путям:
| OS | Путь |
|---|---|
| Windows | C:\Users\%username%\AppData\Roaming\hh-applicant-tool\ |
| macOS | ~/Library/Application Support/hh-applicant-tool/ |
| Linux | ~/.config/hh-applicant-tool/ |
Конфигурационный файл
Конфигурационный называется config.toml. Он содержит основные настройки.
TOML удобен в отличии от JSON для хранения многострочного текста:
setting_name = """
Тут начало рассказа о себе
... Далее идут 100500 строк
И поэтому я считаю, что все п*идорасы
"""
Самый быстрый способ открыть в стандартном редакторе конфиг:
hh-applicant-tool config -e
Путь до него можно найти так:
hh-applicant-tool config -p
# Можно сразу открыть в любимом редакторе
code $(hh-applicant-tool config -p)
TODO: придумать как сохранять комментарии в этом файле при его программном сохранении
Формат файла:
# Прокси для запросов к hh.ru
# Приоритет: флаг --proxy-url, затем этот параметр, затем переменные окружения HTTP_PROXY / HTTPS_PROXY
proxy_url = "http://user:pass@127.0.0.1:8080"
cover_letter = """
{Здравствуйте|Добрый день}, меня зовут %(first_name)s.
{Прошу|Предлагаю} рассмотреть {мою кандидатуру|мое резюме «%(resume_title)s»} на вакансию «%(vacancy_name)s».
У меня есть релевантный опыт работы и интерес к задачам вашей компании.
С уважением,
%(first_name)s
"""
# Тема и текст письма, отправляемого на email компании
apply_mail_subject = "{Отклик|Резюме} на вакансию %(vacancy_name)s"
apply_mail_body = """
{Здравствуйте|Добрый день},
{прошу рассмотреть|пожалуйста рассмотрите} мое резюме %(resume_url)s на вакансию %(vacancy_name)s.
Буду рад ответить на вопросы и пройти собеседование.
"""
#Клиент для раб_оты с API ХХ
# delay может быть переопределен флагом --api-delay
[api_client]
base_url = "https://api.hh.ru/" # адрес API, должен заканчиваться на "/"
delay = 0.345 # пауза между запросами, сек
captcha_cooldown = 3.0 # пауза после решения капчи, сек
max_retries = 3 # число попыток при ошибках соединения
# Отправка писем на email при откликах (--send-email)
[smtp]
host = "smtp.example.com"
port = 465
user = "me@example.com"
password = "secret"
from = "Иван Иванов <me@example.com>" # отправитель, по умолчанию = user
ssl = true # SSL-соединение (обычно порт 465), по умолчанию false
starttls = true # STARTTLS (обычно порт 587), по умолчанию true
# Настройки по умолчанию для работы с OpenAI API
[openai.default]
base_url = "https://api.openai.com/v1/chat/completions" # полный URL эндпоинта chat completions
api_key = "sk-..."
model = "gpt-4o-mini"
proxy_url = "http://127.0.0.1:8080" # отдельный прокси для AI
delay = 0.5 # минимальный интервал между запросами, сек, по умолчанию 0.5
timeout = 60.0 # таймаут запроса, сек, по умолчанию 60
connect_timeout = 10.0 # таймаут соединения, сек, по умолчанию 10
max_retries = 3 # число повторов при сетевых ошибках и лимитах, по умолчанию 3
temperature = 0.0 # по умолчанию 0.0
max_completion_tokens = 1000 # максимальная длина ответа, по умолчанию 1000
# Далее настройки для отдельных случаев
# Секции `openai.*` переопределяют дефолтные из `openai.default`
[openai.captcha]
# Какая-то другая модель лучше подходящая для распознания русского текста
base_url = "..."
api_key = "..."
model = "..."
# И пример передачи истории чата (контекста), которая будет передаваться с каждым вызовом complete
[openai.cover_letter]
system_prompt = "Ты помогаешь писать сопроводительные письма."
# Тут меняем температуру, чтобы сопроводительное письмо напоминало, написанное человеком
temperature = 0.8
[[openai.cover_letter.history]]
role = "user"
content = "Меня зовут Иван, я Python-разработчик."
[[openai.cover_letter.history]]
role = "assistant"
content = "Приятно познакомиться, Иван. Чем помочь?"
# ...
# Ну и далее указываем промпт для генерации сопроводительного
# В нем можно использовать плейсхолдеры, перечисленные в разделе
# шаблоны сообщений
cover_letter_prompt = """
Напиши сопроводительное для отклика на вакансию %(vacancy_name)s ...
"""
Часть настроек скрыта. Некоторые настройки передаются как аргумент конструктора, поэтому если увидите сообщение типа:
TypeError: ApiClient.__init__() got an unexpected keyword argument 'timeoutt'
Значит вы дебил и ебетесь в глаза, допустив в секции api_client ошибку в написании имени настройки...
Значения из конфигов имеют приоритет над флагами, если у флагов дефолтное значение как None отображается. Если значения и в конфиге нет, то будет задано внутренее дефолтное значение.
auth.json
Хранит токены авторизации.
cookies.txt
В cookies.txt хранятся куки, установленные при авторизации на сайте. Они нужны
для решения тестов при отклике на вакансию. Не редактируйте этот файл вручную.
Операция logout не только затирает токены, но очищает cookies.txt.
Логи
В log.txt всегда можно посмотреть подробную информацию об ошибках. В целях безопасности токены, идентификаторы резюме и т. п. затираются. Для просмотра логов используйте операцию log.
База данных
Данные о работодателях и их вакансиях хранятся в файле data. Это обычный с данными базы sqlite3, вы его можете просматривать через DB Browser и т.п., а так же выполнять запросы с помощью операции query.
Что хранится в базе:
- все просмотренные вакансии;
- профили работодателей;
- контакты работодателей;
- данные об откликах;
- пропущенные вакансии (отклонённые фильтром или AI);
- иные данные, важные для работы.
Если этот файл удалить, то сессия не слетит, вы потеряете лишь данные...
Дополнительные настройки
Отключение проверки версии с выводом предупреждения:
hh-applicant-tool settings disable_version_check true
Переменные окружения
Утилита читает следующие переменные окружения:
| Переменная | Описание |
|---|---|
CONFIG_DIR |
Каталог, в котором хранятся данные приложения, вместо пути по умолчанию (см. «Данные приложения»). В Docker задаётся как /app/config |
HH_PROFILE_ID |
Имя подкаталога-профиля внутри CONFIG_DIR (по умолчанию .). Позволяет держать несколько изолированных профилей |
HTTP_PROXY / http_proxy |
Прокси для HTTP-запросов к API HH, если не задан proxy_url в конфиге |
HTTPS_PROXY / https_proxy |
Прокси для HTTPS-запросов к API HH; если не задан — используется HTTP-прокси |
Первые нужны для удобства в Docker. Последние — де факто стандарт.
Шаблоны сообщений
При откликах доступны сл плейсхолдеры (как это на русском?) шаблонов (актуальный список см в apply-vacancies -h):
%(vacancy_name)s: Название вакансии.%(vacancy_url)s: Ссылка на вакансию.%(employer_name)s: Название работодателя.%(first_name)s: Имя пользователя.%(last_name)s: Фамилия пользователя.%(email)s: Email пользователя.%(phone)s: Телефон пользователя.%(resume_title)s: Название резюме.%(resume_hash)s: Идентификатор резюме.%(resume_url)s: Ссылка на резюме.- ...
Например:
Меня заинтересовала ваша вакансия %(vacancy_name)s. Прошу рассмотреть мою кандидатуру. С уважением, %(first_name)s %(last_name)s.
Плейсхолдеры можно использовать в промптах, но не в системных, заданных в секциях openai.*.
Также можно делать текст уникальным с помощью {}. Внутри них через | перечисляются варианты, один из которых будет случайно выбран:
Увольте того {идиота|придурка}, который придумал этот {тупой|сраный|дурацкий} вопрос.
В итоге получится что-то вроде:
Увольте того идиота, который придумал этот сраный вопрос.
{} могут быть вложенными.
Интеграция и использование в скриптах
Если хотите использовать hh-applicant-tool в своих скриптах, то можно это сделать так:
from hh_applicant_tool import HHApplicantTool
tool = HHApplicantTool()
print(tool.api_client.get('/me'))
# Какую-то вспомогательную информацию можно сохранять в настройках
import datetime as dt
tool.storage.settings.set_value("_last_script_run", dt.datetime.now())
# Учтите, что в таком случае токены, которые могут обновиться при выполнении запросов,
# нужно сохранять вручную
tool.save_token()
Команды тоже можно вызывать:
>>> from hh_applicant_tool import HHApplicantTool
>>> HHApplicantTool().run(["auth", "v.pupkin@mail.ru"])
Если же утилита интегрируется в графические приложения, то как правило должна быть возможность отменить текущую операцию:
import multiprocessing as mp
from hh_applicant_tool import HHApplicantTool
def run_command(argv: list[str]):
HHApplicantTool().run(argv)
p = mp.Process(target=run_command, args=(['apply-vacancies', ...],))
p.start()
# Убили процесс
p.terminate()
# Ждем пока не помрет
p.join()
Принцип работы
Долгое время я делал массовые заявки с помощью консоли браузера:
$$('[data-qa="vacancy-serp__vacancy_response"]').forEach((el) => el.click())
Оно работало, хоть и не идеально: например, при отклике на некоторые вакансии перебрасывало на другую страницу. Поэтому я пробовал автоматизировать рассылки через p[yu]ppeteer, пока не прочитал документацию и не обнаружил, что API (интерфейс) содержит все необходимые мне методы. Headhunter позволял создавать свое приложение, но там была ручная модерация, а палиться в нарушении правил пользования сайтом (автоматические отклики запрещены) не хотелось. И тогда я декомпилировал официальное приложение для Android, получил CLIENT_ID и CLIENT_SECRET, необходимые для работы через API.
Позже оказалось, что работы только через API недостаточно. Например, в нем нет методов для решения тех же тестов. Поэтому для их решения приходится эмулировать работу через сайт. Точно так же обстоят дела с решением капчи. API буквально нам предлагает открыть страницу с капчей в браузере, решить ее, а потом вызвать нужный метод API снова.
Сам сайт ХХ использует React. На каждой странице есть тег <template id="HH-Lux-InitialState"></template>. Внутри него заключен JSON {"redirectConfig":....}, в котором разделители заменены на HTML-сущности. В нем содержатся данные, необходимые для рендеринга страницы. Если нужно спарсить какие-то данные с веб-версии (у них она называется десктопной), то искать их стоит там. Там, кстати, много лишнего хранится: от ссылки на бота в Telegram для сотрудников поддержки сайт до страниц каких-то инфоцыган (этот уебанский сайт ставку сделал на зарабатывание денег на соискателях, я это осуждаю в отличии от доения тамошних работодятлов с их тупорылыми херками).
Сами эндпоинты сайта работают довольно странно. Параметры могут передавать через тело, Query String, через куки и заголовки. Но не всегда, на отдельных эндпоинтах обязательна передача в теле запроса.
Так же при авторизации на сайте вам выдается xsrf токен, он используется во всех запросах, отправляемых через XMLHttpRequest...
Для решения капчи нужен captchaState. Его возвращают некоторые эндпоинты сайта к которым обращаются через XMLHttpRequest. Либо он содержится в ссылке. Далее нужно получить captchaKey, послав POST-запрос без тела на эндпоинт сайта /captcha?lang=RU (можно указать еще EN). После получения captchaKey мы грузим изображение /captcha/picture?key=<captchaKey>. Ну и далее решение капчи, captchaState и captchaKey отправляются на POST-запросом на /account/captcha. Этот процесс тут описан упрощенно. Там еще нужно отправлять специальные заголовки... А так сама по себе капча существует словно отдельно от сайта... Ну у них там и микрофронтенды используются... Там говнокодеры ебучие работают.
Самым сложным был "взлом" процесса авторизации. Мне пришлось два часа лазать искать как loginTrustFlags генерируются для "подписи" данных отправляемых при авторизации. Для этого мне пришлось во вкладке отладчика браузера Sources отрелактировать скрипты на сайте, вставив в нужные места console.log/console.dir/console.table перед debugger; (для этого нужно выбрать директорию для синхронизации, тогда станет доступна возможность редактировать скрипты "прямо на сайте"). Там на странице входа из объекта с redirectConfig берется loginTrustFlagsSecret. В нем содержится публичный сертификат, которым шифруется такой JSON:
{"emailOrPhone":{"paste":false,"suggest":true},"ts":Date.now()}
По названиям полей несложно догадаться, что там указывается был ли логин вставлен в поле или выбран из выпадающего списка при вводе. Боты, которые работают через Headless Chromium обычно ничего не вставляют и не выбирают из выпадающего списка, а "печатают"...
При авторизацию по паролю такое шифруется: {"password":{"suggest": false,"paste": false},"ts":...}
Далее этот объект пропускается через JSON.stringify и шифруется с помощью публичного ключа. Используется ассимитричное шифрование RSA-OAEP с хешированием SHA-256 и кодированием в base64. Без этой подписи запроса авторизоваться не получится.
Без расстановки контрольных меток быстро данную функцию шифрования можно найти так: жмем Ctrl-Shift-F во вкладке Sources и вводим await crypto[, ниже будет что-то типа:
// С каждой версией фронтенда имена переменных меняются
const _0x3cd982 = {
..._0x334204,
// 'ts': Date['now']()
'\x74\x73': Date[_0x50c3ac(0x13f, '\x51\x47\x76\x23')](),
};
// Добавляем и смотрим что шифруется в trustLoginFlags...
console.log('trust login: %o', _0x3cd982);
Большинство эндопоинтов шифрует лишь JSON с таймстермп наподобии такого: {"ts":1791234567890}. Я думаю, что кроме ts там обязательных полей нет.
В принципе больше нечего рассказать...
Таким образом моя утилита работает в гибридном режиме: через API и веб-версию. При этом она не использует Headless Chromium ни для авторизации, ни для решения капчи...
Разработка
git clone ...poetry install .- poetry можно установить через pipx
- в дальнейшем можно запускать утилиту так
poetry run hh-applicant-tool authorize - либо можно активировать виртуальное окружение, которое создается в каталоге проекта (
source .venv/bin/activate) - после изменений ничего переустанавливать не надо (
poetry installустанавливает проект в режиме редактирования, те все изменения в каталоге проекта "сразу применяются")
Для публикации новой версиии утилиты на PyPi нужно указывать версию. Она указывается в pyproject.toml. Когда делаете Pull Request, то увеличивайте эту версию на единичку (при малозначительных изменениях — последнее число).
Ответы на заебавшие вопросы
Утилита перестала откликаться на тесты ряяяяяяяяяяяяяя
- Обычно это происходит, когда истекают куки и слетает авторизация на самом сайте (вы долго не запускали отклики). При этом команды типа
whoamiмогут продолжать работать. Нужно по новой авторизоваться.
Ряяяя поиск возвращает не то, что я и ищу, утилита не работает
- Сайт хуй-хуй, созданный для овчарок, и поэтому имеющий красный логотип цвета менстуральных выделений, зарабатывает в тч на услугах по продвижению вакансий, когда вакансию показать некому, то он ее начинает подсовывать калдидатам, которых это "может" заинтересовать (хоть кому-то показать надо), так программистам предлагают вакансии учителей и наставников в онлайн-школах и прочую дичь. Ничего с этим не сделать кроме применения дополнительных фильтров для отсеивания поискового мусора...
- Как работало, так и работает. Меньше читайте всяких пиздаболов
Связанные проекты
- Приложение под Android
- Прога, не требующая установки (скачал и запустил), которая рассылает отклики и отвечает в чаты через хуИИ
Лицензионное соглашение (Limited Non-Commercial License)
Данное программное обеспечение распространяется на условиях ограниченной некоммерческой лицензии.
-
Разрешённое использование
- Личное использование и изучение: Разрешается копирование и модификация кода исключительно для личного ознакомления, обучения и отладки без права публикации или распространения изменений.
- Интеграция в программное обеспечение: Разрешается включение данного кода в состав сторонних бесплатных некоммерческих продуктов только при полном сохранении исходного кода в неизменном виде.
- Атрибуция: Любое распространение, демонстрация или использование кода (в том числе в составе других продуктов) допускается только при обязательном указании автора и ссылки на оригинальный репозиторий.
-
Ограничения на использование Строго запрещается использование данного кода в любых коммерческих целях без предварительного письменного согласия автора, включая, но не ограничиваясь:
- Интеграцией в платные сервисы, приложения, веб-сайты либо использованием в рамках оказания платных услуг.
- Публикацией, распространением или передачей третьим лицам модифицированных (изменённых) версий кода.
- Перепродажей, сублицензированием либо использованием кода в интересах коммерческих организаций.
-
Отказ от ответственности Программное обеспечение предоставляется по принципу «как есть» (AS IS). Автор не несёт ответственности за любые прямые или косвенные убытки, включая, но не ограничиваясь, блокировкой аккаунтов, потерей данных или иными негативными последствиями, возникшими в результате использования данного программного обеспечения.
© 2023-2026 Sergey M. Все права защищены.
Metadata
Release files for hh-applicant-tool 2.0.12
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hh_applicant_tool-2.0.12.tar.gz | 144.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hh_applicant_tool-2.0.12-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 305.7 kB
Release files / hh_applicant_tool-2.0.12.tar.gz
| Download URL | hh_applicant_tool-2.0.12.tar.gz |
|---|---|
| Size | 144.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3034bee6117b5c7874a14d0df32f7eefcc70eddc08c11d522fb65d90ae49cb49
|
|
BLAKE2b-256 checksum How to use checksums |
31bb6e967d9dc13c0c8d48361046584140ccb62f71246ae22ed18b4826dd100c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.13.1 Linux/6.17.0-1022-azure
|
Release files / hh_applicant_tool-2.0.12-py3-none-any.whl
| Download URL | hh_applicant_tool-2.0.12-py3-none-any.whl |
|---|---|
| Size | 161.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5cbbe8e031fa71b3bece9b3bc76af98d16e020ea9c7ae64e52b62095885eeec8
|
|
BLAKE2b-256 checksum How to use checksums |
fe8f365a65643f1fd78cda5aa7ec9b68def592e68d15897643102356c19a642d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.13.1 Linux/6.17.0-1022-azure
|