Skip to content

valeksan/hh-skill-parser

Repository files navigation

HH Skill Parser

Этот скрипт собирает вакансии с HH.ru по ключевым запросам, извлекает из них технические навыки и сохраняет результаты в CSV. При наличии matplotlib он также может построить PNG-график.

Особенности

  • Поддерживает несколько источников данных: API HH и HTML-страницы HH.
  • Фильтрация по whitelist навыков.
  • Поддержка сохранения прогресса — если скрипт прервётся, при перезапуске он продолжит с места остановки.
  • Построение графика опционально: если matplotlib не установлена или передан --no-chart, скрипт всё равно сохранит CSV.
  • Анимация в консоли опциональна: если console-animation не установлена, скрипт всё равно работает.
  • HTML fallback для случаев, когда api.hh.ru блокируется через ddos-guard.

Установка

  1. Клонируйте репозиторий:

    git clone --depth=1 https://github.com/valeksan/hh-skills-parser.git
    cd hh-skills-parser
  2. Создайте виртуальное окружение и активируйте его:

    python -m venv .venv
    source .venv/bin/activate  # Linux/Mac
    # или
    .venv\Scripts\activate     # Windows
  3. Установите проект через pyproject.toml:

    # Базовая установка
    pip install -e .
    
    # Только поддержка графиков
    pip install -e ".[chart]"
    
    # Только консольная анимация
    pip install -e ".[cli]"
    
    # Только сборка бинарника
    pip install -e ".[bundle]"
    
    # Полная установка: график + консольная анимация
    pip install -e ".[full]"
  4. Или используйте make:

    make install
    make install-chart
    make install-cli
    make install-bundle
    make install-full

Makefile

Проект содержит готовые команды для установки и запуска:

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-зависимости, но не заменяет ваши входные данные.

Использование

  1. Настройка
  • queries.txt — список поисковых запросов, по одному на строку.
  • skills_whitelist.txt — список навыков для режима --mode description. Для key-skills не обязателен.

Как выбрать режим (--mode)

  • 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 в этом случае нормален: вакансия считается обработанной, даже если в ней не нашлось навыков.
  1. Запуск

    # Стандартный запуск
    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
  2. Типовые рабочие команды

    # Комбинированный сбор навыков без двойного учёта по одной вакансии
    ./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

  1. Результаты
  • CSV со всеми навыками: top_skills_all_data.csv
  • PNG-график: hh_skills_bar_chart.png (если график не отключён)
  • Прогресс обработки: progress.json

Smoke-тесты

Для быстрой локальной проверки CLI и базовой обработки данных:

make smoke

Тесты не ходят в сеть и проверяют только локально воспроизводимые сценарии.

Environment variables

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

Пример:

cp .env.example .env
./parse_skills.py

Если нужен другой путь, используйте --env-file custom.env. Чтобы полностью отключить загрузку .env, передайте --no-dotenv.

LOGLEVEL

Принимает одно из значений warning, error, critical, info, debug. По умолчанию LOGLEVEL=info, чтобы в консоли был виден прогресс парсинга.

Пример использования:

LOGLEVEL=info ./parse_skills.py

HH_NO_CHART

Если установить HH_NO_CHART=1, скрипт не будет строить PNG-график и ограничится CSV-выгрузкой.

Пример:

HH_NO_CHART=1 ./parse_skills.py

HTTPS_PROXY / HTTP_PROXY

Опциональный прокси для requests, если текущий IP блокируется внешним антиботом.

Пример:

HTTPS_PROXY='http://127.0.0.1:8080' ./parse_skills.py

Важно: 127.0.0.1:8080 здесь только пример. Такой адрес сработает лишь если у вас уже запущен локальный proxy-сервер на этом порту.

Зависимости и extras

Проект описан через pyproject.toml.

  • Базовые зависимости:
    • requests
    • beautifulsoup4
  • Опциональные extras:
    • chart — добавляет matplotlib
    • cli — добавляет console-animation
    • bundle — добавляет pyinstaller
    • full — устанавливает всё сразу

Формат файлов

queries.txt

Список поисковых запросов (по одному на строке). Строки, начинающиеся с #, игнорируются.

Пример:

# Вакансии Data Science
Data Scientist
Machine Learning
ML Engineer

skills_whitelist.txt

Список навыков для анализа (по одному на строке). Строки, начинающиеся с #, игнорируются.

Пример:

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

Причины ошибок

Ошибки HTTP 403 (Forbidden) при работе с API HH.ru могут возникать по нескольким причинам:

  1. Отсутствие или неверные заголовки запроса — сервер может блокировать запросы с неудачным набором заголовков или подозрительным профилем клиента.
  2. Rate limiting (ограничение частоты запросов) — слишком частые запросы приводят к временной блокировке.
  3. Подозрительная активность — запросы с одинаковыми параметрами, выполняемые в быстрой последовательности, могут быть расценены как бот.
  4. Внешняя антибот-защита — запрос может быть заблокирован не самим API, а фильтром вроде ddos-guard на уровне IP / TLS-профиля / репутации канала.
  5. Изменения в API — HH.ru может менять требования к запросам без предварительного уведомления.

Внесенные изменения

Для смягчения таких ошибок в проекте добавлены:

  1. Добавление более корректных заголовков — в requests.Session добавлены:

    • браузерный User-Agent
    • Referer, Origin, Accept, Accept-Language, Accept-Encoding, Connection
  2. Механизм повторных попыток с экспоненциальной задержкой — реализован декоратор @retry_request, который:

    • Повторяет запрос при ошибках 403, 429 (Too Many Requests) и 5xx (серверные ошибки)
    • Использует экспоненциальную задержку с базой 1 секунда и максимумом 30 секунд
    • Обрабатывает заголовок Retry-After при ошибке 429
  3. Настраиваемые задержки между запросами:

    • между страницами поиска: --page-delay-min / --page-delay-max
    • между запросами деталей вакансий: --vacancy-delay-min / --vacancy-delay-max
  4. Таймауты и прокси:

    • --request-timeout задаёт таймаут HTTP-запроса
    • --proxy или переменные окружения HTTPS_PROXY / HTTP_PROXY позволяют сменить канал доступа
  5. 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-вакансий
  6. Улучшенное логирование — при ошибке 403 логируется первые 500 символов тела ответа; если ответ пришёл через ddos-guard, это отмечается отдельно.

Механизм работы

  1. Сессия с заголовками создается при запуске скрипта и используется для всех запросов.
  2. Декоратор @retry_request применяется к функции fetch_data, которая выполняет HTTP-запросы.
  3. При возникновении ошибки 403:
    • Запрос повторяется через увеличивающуюся задержку (1с, 2с, 4с... до 30с)
    • После максимального числа попыток (по умолчанию 3) исключение пробрасывается дальше
  4. Случайные задержки между страницами поиска и карточками вакансий снижают вероятность блокировки.
  5. Если 403 возвращается с заголовком server: ddos-guard, скрипт сообщает, что проблема, вероятно, уже вне логики запроса.
  6. Если включён --source auto, после блокировки API скрипт пробует продолжить работу через HTML-страницы вакансий.

Рекомендации

Если вы продолжаете получать ошибки 403:

  1. Увеличьте задержки — измените параметры base_delay и max_delay в декораторе @retry_request.
  2. Поднимите интервалы между запросами — например --page-delay-min 5 --page-delay-max 12 --vacancy-delay-min 2 --vacancy-delay-max 6.
  3. Проверьте заголовки — убедитесь, что заголовки актуальны и соответствуют текущим требованиям HH.ru.
  4. Используйте прокси — если ваш IP-адрес блокируется внешним фильтром, настройте --proxy или HTTPS_PROXY.
  5. Ограничьте количество запросов — уменьшите vacancies_limit или количество запросов в queries.txt.
  6. Если видите ProxyError ... 127.0.0.1:8080 connection refused, это значит, что локальный прокси не запущен. В таком случае уберите HTTPS_PROXY/--proxy или укажите реальный прокси.
  7. Если нужен максимально живучий режим с текущего IP, попробуйте --source html --mode description.
  8. Если вы хотите оставить --mode key-skills, но не терять HTML-результаты, добавьте --html-description-fallback.

Дополнительные меры

  1. Прокси или другой IP-канал — если видите server: ddos-guard, это самый вероятный следующий шаг.
  2. Распределение запросов во времени — запускать скрипт в периоды низкой нагрузки или с большими интервалами.
  3. Использование официального API HH.ru — если для нужного сценария доступна авторизация приложения/пользователя, это надёжнее обычного анонимного сбора.
  4. HTML fallback лучше для description, чем для key-skills — в HTML-страницах вакансий поле key_skills часто отсутствует, а описание почти всегда доступно.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Соавторы

Этот проект получил вклад от следующих соавторов:

  • tonakihan — исправления и улучшения кода

Если вы внесли вклад в проект, не стесняйтесь добавить себя в этот список через pull request.

Автор

Виталий (@valeksan)

About

Parser for extracting & analyzing tech skills from HH.ru job listings

Topics

Resources

License

Stars

Watchers

Forks

Contributors