Этот скрипт собирает вакансии с HH.ru по ключевым запросам, извлекает из них технические навыки и сохраняет результаты в CSV. При наличии matplotlib он также может построить PNG-график.
- Поддерживает несколько источников данных: API HH и HTML-страницы HH.
- Фильтрация по whitelist навыков.
- Поддержка сохранения прогресса — если скрипт прервётся, при перезапуске он продолжит с места остановки.
- Построение графика опционально: если
matplotlibне установлена или передан--no-chart, скрипт всё равно сохранит CSV. - Анимация в консоли опциональна: если
console-animationне установлена, скрипт всё равно работает. - HTML fallback для случаев, когда
api.hh.ruблокируется черезddos-guard.
-
Клонируйте репозиторий:
git clone --depth=1 https://github.com/valeksan/hh-skills-parser.git cd hh-skills-parser -
Создайте виртуальное окружение и активируйте его:
python -m venv .venv source .venv/bin/activate # Linux/Mac # или .venv\Scripts\activate # Windows
-
Установите проект через
pyproject.toml:# Базовая установка pip install -e . # Только поддержка графиков pip install -e ".[chart]" # Только консольная анимация pip install -e ".[cli]" # Только сборка бинарника pip install -e ".[bundle]" # Полная установка: график + консольная анимация pip install -e ".[full]"
-
Или используйте
make:make install make install-chart make install-cli make install-bundle make install-full
Проект содержит готовые команды для установки и запуска:
make help
make install
make install-chart
make install-cli
make install-bundle
make install-full
make run
make run-html
make run-key-skills
make run-lite
make smoke
make bundle
make cleanМожно собрать один исполняемый файл со всеми Python-зависимостями внутри через PyInstaller.
make install-bundle
make bundleРезультат появится в dist/hh-skill-parser.
Если pyinstaller не установлен, make bundle не упадёт молча, а подскажет, как поставить нужный пакет.
Если в бинарнике нужна поддержка графика и консольной анимации, сначала установите соответствующие extras, например:
make install-full
make install-bundle
make bundleВажно: queries.txt, skills_whitelist.txt, .env и выходные файлы остаются внешними файлами рядом с запуском. Бинарник упаковывает код и Python-зависимости, но не заменяет ваши входные данные.
- Настройка
queries.txt— список поисковых запросов, по одному на строку.skills_whitelist.txt— список навыков для режима--mode description. Дляkey-skillsне обязателен.
key-skills:- берёт навыки только из структурированного поля
key_skillsвакансии; - обычно даёт более "чистые" навыки;
- не использует
skills_whitelist.txt.
- берёт навыки только из структурированного поля
description:- ищет навыки в тексте описания вакансии;
- использует
skills_whitelist.txt; - обычно даёт больше находок, но возможны ложные совпадения.
both:- объединяет навыки из
key_skillsиdescription; - каждый навык считается один раз на одну вакансию (
id); - удобно, когда хочешь максимум сигнала без двойного подсчёта.
- объединяет навыки из
Практическое правило:
- если API HH доступен, можно использовать
key-skills; - если API блокируется и работа идёт через HTML fallback, чаще лучше
description.
Почему так:
- в HTML-ветке поле
key_skillsчасто пустое/недоступно; - поэтому при
--mode key-skillsитог может быть пустым (current_skill_counts: {}), даже еслиprocessed_vacancy_idsрастёт. - рост
processed_vacancy_idsв этом случае нормален: вакансия считается обработанной, даже если в ней не нашлось навыков.
-
Запуск
# Стандартный запуск python parse_skills.py # Если проект установлен как пакет, доступен entry point hh-skill-parser # На UNIX ./parse_skills.py # Для получения справки ./parse_skills.py --help # Если API HH блокируется, можно сразу использовать HTML-источник ./parse_skills.py --source html --mode description # Если выбран key-skills, но вакансии приходят из HTML, # можно автоматически переключаться на description ./parse_skills.py --source auto --mode key-skills --html-description-fallback # Комбинированный режим: key_skills + description с дедупом на вакансию ./parse_skills.py --mode both # Минимальный режим без PNG-графика ./parse_skills.py --no-chart
-
Типовые рабочие команды
# Комбинированный сбор навыков без двойного учёта по одной вакансии ./parse_skills.py --mode both --source auto --html-description-fallback # Самый живучий режим при блокировке API ./parse_skills.py --source html --mode description --no-chart # Если хочется оставить key-skills, но не терять HTML-результаты ./parse_skills.py --source auto --mode key-skills --html-description-fallback # Если ранее был пустой прогресс и много processed_vacancy_ids rm -f progress.json ./parse_skills.py --source html --mode description
- Результаты
- CSV со всеми навыками:
top_skills_all_data.csv - PNG-график:
hh_skills_bar_chart.png(если график не отключён) - Прогресс обработки:
progress.json
Для быстрой локальной проверки CLI и базовой обработки данных:
make smokeТесты не ходят в сеть и проверяют только локально воспроизводимые сценарии.
Скрипт автоматически загружает переменные из файла .env в корне проекта, если он существует.
Это удобно для локального хранения сетевых настроек без длинной команды запуска.
Пример:
cp .env.example .env
./parse_skills.pyЕсли нужен другой путь, используйте --env-file custom.env. Чтобы полностью отключить загрузку .env, передайте --no-dotenv.
Принимает одно из значений warning, error, critical, info, debug.
По умолчанию LOGLEVEL=info, чтобы в консоли был виден прогресс парсинга.
Пример использования:
LOGLEVEL=info ./parse_skills.pyЕсли установить HH_NO_CHART=1, скрипт не будет строить PNG-график и ограничится CSV-выгрузкой.
Пример:
HH_NO_CHART=1 ./parse_skills.pyОпциональный прокси для requests, если текущий IP блокируется внешним антиботом.
Пример:
HTTPS_PROXY='http://127.0.0.1:8080' ./parse_skills.pyВажно: 127.0.0.1:8080 здесь только пример. Такой адрес сработает лишь если у вас уже запущен локальный proxy-сервер на этом порту.
Проект описан через pyproject.toml.
- Базовые зависимости:
requestsbeautifulsoup4
- Опциональные extras:
chart— добавляетmatplotlibcli— добавляетconsole-animationbundle— добавляетpyinstallerfull— устанавливает всё сразу
Список поисковых запросов (по одному на строке). Строки, начинающиеся с #, игнорируются.
Пример:
# Вакансии Data Science
Data Scientist
Machine Learning
ML EngineerСписок навыков для анализа (по одному на строке). Строки, начинающиеся с #, игнорируются.
Пример:
python
sql
pandas
...Поиск выполняется по запросу из queries.txt, а затем дополнительно фильтруется по названию вакансии. Это нужно, потому что HH иногда возвращает нерелевантные результаты.
Дополнительный фильтр разбивает запрос на слова, и в названии вакансии должно совпасть хотя бы одно слово из запроса.
queries.txt
"Front-end developer"
#Преобразуется в
"Front", "end", "developer"
При --mode key-skills используются навыки из поля key_skills вакансии HH.ru. Это ближе всего к тем навыкам, которые можно подтвердить в резюме.
При --mode description поиск выполняется по тексту описания вакансии и проверяется через skills_whitelist.txt. Возможны ложные срабатывания,
так как анализируется обычный текст без семантической нормализации. Например, c как язык программирования теоретически можно спутать с кириллической с.
Ошибки HTTP 403 (Forbidden) при работе с API HH.ru могут возникать по нескольким причинам:
- Отсутствие или неверные заголовки запроса — сервер может блокировать запросы с неудачным набором заголовков или подозрительным профилем клиента.
- Rate limiting (ограничение частоты запросов) — слишком частые запросы приводят к временной блокировке.
- Подозрительная активность — запросы с одинаковыми параметрами, выполняемые в быстрой последовательности, могут быть расценены как бот.
- Внешняя антибот-защита — запрос может быть заблокирован не самим API, а фильтром вроде
ddos-guardна уровне IP / TLS-профиля / репутации канала. - Изменения в API — HH.ru может менять требования к запросам без предварительного уведомления.
Для смягчения таких ошибок в проекте добавлены:
-
Добавление более корректных заголовков — в
requests.Sessionдобавлены:- браузерный
User-Agent Referer,Origin,Accept,Accept-Language,Accept-Encoding,Connection
- браузерный
-
Механизм повторных попыток с экспоненциальной задержкой — реализован декоратор
@retry_request, который:- Повторяет запрос при ошибках 403, 429 (Too Many Requests) и 5xx (серверные ошибки)
- Использует экспоненциальную задержку с базой 1 секунда и максимумом 30 секунд
- Обрабатывает заголовок
Retry-Afterпри ошибке 429
-
Настраиваемые задержки между запросами:
- между страницами поиска:
--page-delay-min/--page-delay-max - между запросами деталей вакансий:
--vacancy-delay-min/--vacancy-delay-max
- между страницами поиска:
-
Таймауты и прокси:
--request-timeoutзадаёт таймаут HTTP-запроса--proxyили переменные окруженияHTTPS_PROXY/HTTP_PROXYпозволяют сменить канал доступа
-
Fallback на HTML-страницы HH:
--source autoсначала пытается использовать API HH;- при блокировке
api.hh.ruпереключается на HTML-поискhh.ru/search/vacancy; --source htmlпозволяет сразу идти в HTML-режим--html-description-fallbackавтоматически меняетkey-skillsнаdescriptionдля HTML-вакансий
-
Улучшенное логирование — при ошибке 403 логируется первые 500 символов тела ответа; если ответ пришёл через
ddos-guard, это отмечается отдельно.
- Сессия с заголовками создается при запуске скрипта и используется для всех запросов.
- Декоратор
@retry_requestприменяется к функцииfetch_data, которая выполняет HTTP-запросы. - При возникновении ошибки 403:
- Запрос повторяется через увеличивающуюся задержку (1с, 2с, 4с... до 30с)
- После максимального числа попыток (по умолчанию 3) исключение пробрасывается дальше
- Случайные задержки между страницами поиска и карточками вакансий снижают вероятность блокировки.
- Если
403возвращается с заголовкомserver: ddos-guard, скрипт сообщает, что проблема, вероятно, уже вне логики запроса. - Если включён
--source auto, после блокировки API скрипт пробует продолжить работу через HTML-страницы вакансий.
Если вы продолжаете получать ошибки 403:
- Увеличьте задержки — измените параметры
base_delayиmax_delayв декораторе@retry_request. - Поднимите интервалы между запросами — например
--page-delay-min 5 --page-delay-max 12 --vacancy-delay-min 2 --vacancy-delay-max 6. - Проверьте заголовки — убедитесь, что заголовки актуальны и соответствуют текущим требованиям HH.ru.
- Используйте прокси — если ваш IP-адрес блокируется внешним фильтром, настройте
--proxyилиHTTPS_PROXY. - Ограничьте количество запросов — уменьшите
vacancies_limitили количество запросов вqueries.txt. - Если видите
ProxyError ... 127.0.0.1:8080 connection refused, это значит, что локальный прокси не запущен. В таком случае уберитеHTTPS_PROXY/--proxyили укажите реальный прокси. - Если нужен максимально живучий режим с текущего IP, попробуйте
--source html --mode description. - Если вы хотите оставить
--mode key-skills, но не терять HTML-результаты, добавьте--html-description-fallback.
- Прокси или другой IP-канал — если видите
server: ddos-guard, это самый вероятный следующий шаг. - Распределение запросов во времени — запускать скрипт в периоды низкой нагрузки или с большими интервалами.
- Использование официального API HH.ru — если для нужного сценария доступна авторизация приложения/пользователя, это надёжнее обычного анонимного сбора.
- HTML fallback лучше для
description, чем дляkey-skills— в HTML-страницах вакансий полеkey_skillsчасто отсутствует, а описание почти всегда доступно.
This project is licensed under the MIT License - see the LICENSE file for details.
Этот проект получил вклад от следующих соавторов:
- tonakihan — исправления и улучшения кода
Если вы внесли вклад в проект, не стесняйтесь добавить себя в этот список через pull request.