Автоматизируем HeadHunter: Как я перестал скроллить вакансии и написал для OpenClaw свой парсер на Python

Жарим рака
Жарим рака

Сегодня я листал сайт HeadHunter и у меня устала рука перебирать тысячи вакансий в Москве и Питере. «Я ж программист. Поищу простой путь», — подумал я, лежа в кровати.

Такой путь был найден — у HH есть открытый API, который позволяет делать много чего без OAuth-авторизации.

Я попробую написать приложение на Python, которое добавлю в качестве скилла для моего OpenClaw и поставлю в Cron, чтобы каждое утро у меня была свежая и актуальная подборка вакансий по ключевым словам.

Никаких левых приложений, трекеров отслеживания и тому подобной фигни. Только магия!

Коротко об API и ресурсах

API у HeadHunter на удивление человеческий - не придется регистрировать приложение и возиться с токенами.

  • Документация на GitHub: hhru/api — основной источник истины.
  • Базовый URL: https://api.hh.ru/ — сюда мы будем слать наши запросы.
  • Справочники: api.hh.ru/dictionaries — здесь лежат коды валют, опыта работы и прочие системные штуки.

Мой стек: Асинхронность и база данных

Писать обычный «линейный» скрипт скучно и медленно, про первые результаты я уже писал в своём авторском Tg-канале.

Зайдем сразу с козырей.

Я буду использовать асинхронный подход на aiohttp.

В качестве хранилища — MongoDB.

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

Немного кода для понимания процесса

Самое важное при работе с API HH — это честный User-Agent. Без него вас просто «отфутболят» с ошибкой 400.

Выбирай любой useragent
Выбирай любой useragent
# Мой заголовок, чтобы API понимало, кто к нему стучится headers = { 'User-Agent': 'MyCareerScraper/1.2 (contact: my-dev-mail@example.com)', 'Accept': 'application/json' } async def fetch_vacancy_detail(session, vacancy_id): """Тут я лезу за подробностями каждой вакансии по её ID""" url = f'https://api.hh.ru/vacancies/{vacancy_id}' async with session.get(url, headers=headers) as response: if response.status == 200: # Парсим JSON и забираем только то, что мне реально нужно data = await response.json() return { "hh_id": vacancy_id, "title": data.get('name'), "skills": [s['name'] for s in data.get('key_skills', [])], "description": clean_html(data.get('description', '')) } return None

А вот так я решаю проблему дубликатов.

Перед тем как сохранить новую вакансию, я проверяю её по hh_id. В MongoDB это делается красиво через уникальный индекс.

# Создаю уникальный индекс, чтобы база сама следила за порядком await collection.create_index("hh_id", unique=True) # И вставляю данные, не боясь повторов try: await collection.insert_one(doc) except Exception as e: # Если вакансия уже есть — просто идем дальше pass

Важные нюансы и ограничения

Работая с API такого гиганта, нужно соблюдать правила приличия, иначе бан прилетит быстрее, чем вы допьете кофе:

  1. Rate Limits: HH не любит, когда в него "пулеметят" запросами. Даже в асинхронном режиме я стараюсь не наглеть. Если ловите 403 — значит, пора притормозить.
  2. Глубина поиска: API не отдаст вам миллион вакансий. Обычно лимит — это 2000 записей (например, 20 страниц по 100 вакансий). Для точечного поиска "под себя" этого за глаза.
  3. Мусор в описании: Текст вакансии приходит в HTML. Приходится использовать регулярки, чтобы вычистить теги <li>, <strong> и прочий визуальный шум.
  4. User-Agent: Повторюсь, это критично. Не используйте дефолтные заголовки библиотек типа python-requests. Придумайте что-то свое.

OpenClaw о котором все слышали, но мало кто пробовал

Чтобы не отставать от прогресса, я развернул на выделенном сервере Ubuntu 24 TLS, поставил туда OpenClaw и MondoDB в Docker.
Этапы установки настройки я смело пропущу - это изи и выполняется в пару приёмов.

Статус работающего Openclaw
Статус работающего Openclaw

Теперь попросим Openclaw выполнить за нас рутину. Поскольку у меня уже установлен Openclaw на выделенном сервере и настроена интеграция с Ai и Telegram, я пишу своему Telegram-боту промпт:

Я подговил python-скрипты в директории ~/hh_parser

headhunter.py - это скрипт для поиска вакансий по ключевым словам keyword

vacancy_report.py - скрипт который достает загруженные данные из Mongodb и создает файл vacancy_report.md

Твоя задача: создай skill для поиска работы используя эти скрипты и добавить их в планировщик. Запуск каждое утро в 7:00

Полученный markdown-файл отправляй в чат.

Выполни необходимые действия и проверь работу скриптов

В ответ от него получаю:

Ответ от бота
Ответ от бота

После небольшой работы и пары минут ожидания в чат получаю месседж:

Результат работы Skill
Результат работы Skill

В формате markdown в Obsidian выглядит лучше :-)

Списки вакансий
Списки вакансий

Конфигурация поиска

KEYWORDS = 'NAME:(DevOps OR "Системный администратор") AND Python'EXPERIENCE = 'between1And3' # 1-3 года SCHEDULE = 'remote' # Удалённая работа AREA = 113 # Вся Россия

Вывод (Иго-го)

Теперь мой сервер на Ubuntu каждое утро шуршит скриптом.

Результат — чистый и понятный Markdown-файл с аналитикой: сколько новых вакансий появилось, какие навыки сейчас в топе и где предлагают самую приятную «вилку» по зарплате.

Весь этот массив данных аккуратно оседает в MongoDB, и я всегда могу поднять историю за месяц, чтобы понять, куда движется рынок ИТ и ИБ в моем регионе, написать frontend для визуализации и т.п.

Автоматизация — это не всегда про сложные энтерпрайз-системы. Иногда это просто способ сэкономить себе 20 минут утреннего скроллинга и потратить их на что-то более полезное.

Возможно, следующим этапом научу агента откликаться на вакансии за меня... Следите за новостями.

p.s. Жаль что не все компании идут в ногу со временем, не хотят развиваться и отказывают в работе (Роснефть превед)

Бонус

Ссылку на репозиторий ищите в Telegram-канале.

Хотите знать больше?

Подписывайтесь на мой канал в Я.Дзен, Teletype и Telegram и ссылка на канал МАХ (если TG всё же заблокируют).

Следите за моими идеями и сделками на TradingView

ВАЖНО! Не является инвестиционной или финансовой рекомендацией. Все описанное - только отражение личного опыта автора и его размышления. Я никого не убеждаю и не побуждаю к действиям. Любые инвестиции это только Ваш выбор.

@drunkenpin
3
3 комментария