Парсинг HH.ru: как собирать данные о вакансиях и резюме без боли и судебных рисков

Парсинг HH.ru: как собирать данные о вакансиях и резюме без боли и судебных рисков

Многие считают, что собрать данные с hh.ru — задача на вечер: requests, BeautifulSoup, цикл по страницам, готово. На практике первый же запуск приносит пустые списки при ответе 200 OK, второй — бан по IP, а третий — претензию от юриста. HeadHunter — это не «обычный сайт», а крупная база с миллионами записей, регулируемая ФЗ-152 и собственным пользовательским соглашением. Дальше — последовательный разбор того, что и как собирать с площадки, какие ограничения встроены изначально, где проходит правовая красная линия и как выглядит парсер, который доживает до второго месяца эксплуатации.

Содержание

Что такое HH.ru с точки зрения данных

HeadHunter — крупнейшая площадка поиска работы в русскоязычном сегменте. По разным оценкам, на ней одновременно живёт от 1,1 до 1,2 млн активных вакансий, около 60–86 млн резюме и порядка 380 тысяч компаний, а ежедневно появляется примерно 25 тысяч новых вакансий. Платформа покрывает около 80% рынка СНГ, и именно поэтому она остаётся первой целью для рекрутеров, аналитиков и исследователей рынка труда. К той же экосистеме относится zarplata.ru, принадлежащая HH и работающая на тех же протоколах.

На уровне данных площадка устроена иерархично: страны, регионы, города, работодатели, вакансии, резюме, словари справочников. Для парсера это означает, что любая прикладная задача упирается в один из четырёх объектов:

  • Вакансия — публичный объект, описывающий должность, работодателя, регион, вилку зарплаты, опыт, график, требования и описание;
  • Резюме — частично публичный, частично закрытый объект, содержащий персональные данные;
  • Работодатель — карточка компании со списком открытых вакансий и иногда рейтингом;
  • Справочник — закрытые таблицы с типами занятости, профессиональными ролями, графиком и регионами.

Эта классификация важна не сама по себе. Именно от типа объекта зависит и метод сбора, и правовой режим, и допустимый объём. Вакансии можно собирать через документированный API в неограниченных по сравнению с резюме объёмах, тогда как массовый сбор резюме без согласия субъекта прямо запрещён законом.

Иерархия методов: с чего начинать

Самый дорогой по времени и поддержке способ собрать данные с HH — это парсить HTML-разметку напрямую. Даниил Охлопков в материале «Как спарсить любой сайт?» предлагает универсальный алгоритм, отлично работающий и на HH:

  1. Найти официальный API.
  2. Если API нет — найти XHR-запросы в DevTools браузера.
  3. Если XHR не помог — поискать сырой JSON, встроенный в HTML.
  4. Если страница рендерится только в JavaScript — использовать headless-браузер.
  5. HTML-парсинг — последний вариант, к которому стоит обращаться, только когда всё перечисленное недоступно.

В случае HH.ru шаг номер один срабатывает почти всегда: у площадки есть зрелый REST API на api.hh.ru с документацией. Это снимает основные риски: и юридические (доступ через API не нарушает оферту), и технические (контракт API меняется реже, чем CSS-классы на странице).

Тогда зачем вообще нужны остальные шаги? Затем, что часть данных через API не отдаётся. Контакты и фотографии в резюме, расширенные карточки работодателей, рейтинги и отзывы — это всё либо платные тарифы, либо то, что скрейпится напрямую. Здесь и появляется реальная необходимость в headless и HTML-парсинге, но уже как дополнении, а не основе.

Базовое правило. Если задача решается через API — её надо решать через API. На длинной дистанции скрейпер HTML обходится дороже не потому, что его сложнее написать, а потому что его нужно постоянно чинить: HH меняет CSS-классы (например, vacancy-serp-item сменился на magritte-redesign и f-test-search-result-item) каждые несколько месяцев.

Официальный API: эндпоинты, токены, поля

Документация API лежит в официальном репозитории hhru/api. Регистрация приложения происходит на dev.hh.ru, где выдаются client_id и client_secret. Для работы с публичными данными — вакансиями, регионами, справочниками — токен формально не обязателен, но без авторизации заметно жёстче лимиты. Для приватных данных, таких как полные резюме, отклики (/negotiations) или личные заметки, нужен полноценный OAuth 2.0 (Open Authorization) с обновлением access_token примерно раз в 14 дней через refresh_token.

Структура запроса

Базовый запрос на поиск вакансий выглядит так:

GET https://api.hh.ru/vacancies?text=Python&area=1&per_page=100&page=0

Параметры text, area, per_page и page закрывают около 80% реальных задач. Чем точнее выставлены фильтры — тем меньше вакансий вернётся в одной выдаче, и тем дальше парсер сможет дойти до конца, не упёршись в потолок пагинации (об этом ниже).

Параметр area: 9 «стран» и 4 тысячи городов

Регионы у HH организованы трёхуровнево: страна, регион, город. Корневых стран девять, и их идентификаторы стоит держать под рукой:

IDСтрана
5Украина
9Азербайджан
16Беларусь
28Грузия
40Казахстан
48Кыргызстан
97Узбекистан
113Россия
1001Другие регионы

Внутри России — больше 4 тысяч населённых пунктов, у каждого свой id. Москва — это 1, Санкт-Петербург — 2. Вытащить полное дерево регионов можно одним запросом к /areas; результат удобно сохранить в локальный JSON и больше не дёргать API ради справочника.

Поля вакансии

Объект вакансии возвращается уже структурированным. Чаще всего парсятся следующие поля:

ПолеТипЧто содержит
idstringИдентификатор; формирует ссылку hh.ru/vacancy/<id>
namestringНазвание позиции
employerobjectid и name работодателя
areaobjectid и name региона
salaryobjectfrom, to, currency, gross (true=до НДФЛ)
experiencestring«noExperience», «between1And3» и т. д.
schedule / employmentstringГрафик и тип занятости
key_skillslistСписок навыков, заявленных вакансией
snippetobjectКороткий requirement + responsibility
descriptionstringПолное описание (HTML, требует очистки)
published_atstringISO-8601, ключ для инкрементальной выгрузки

Особое внимание стоит уделить полю salary. Зарплата может быть указана в RUB, USD, EUR или UAH, при этом флаг gross определяет, заявлена ли сумма «грязными» или «на руки». Для адекватной аналитики оба значения обычно нормализуют в рубли «после НДФЛ» по текущему курсу. Именно так это сделано в открытом проекте hh_research — там же показан вариант с предсказанием зарплаты для вакансий, в которых она не указана.

Семантика поиска через text

Параметр text — это не просто строка, а мини-язык запросов. Помимо привычного text=Python, поддерживаются:

  • text=NAME:python — поиск только в названии вакансии;
  • text=COMPANY_NAME:Яндекс — поиск только по работодателю;
  • text=DESCRIPTION:django — поиск только в теле описания;
  • text=("QA" OR "Тестировщик") NOT ("Junior" OR "Стажер") — булева алгебра с группировкой;
  • text=!python — точное совпадение, без морфологии.

Эта возможность существенно сокращает количество шумных совпадений. На наивный запрос text=Python выдача легко возвращает «Senior Analyst, базовое знание SQL, Python будет плюсом», что прекрасно описано в кейсе бота «Аврора»: автор пишет, что с обычным запросом получает «500 вакансий не по теме» при поиске «Python Developer без опыта». Решение там оказалось гибридным: сначала идёт /resumes/{id}/similar_vacancies, потом — обычный /vacancies, и переключение происходит бесшовно при исчерпании первого источника.

Лимиты и подводные камни

Любой парсер HH рано или поздно упирается в один из четырёх лимитов. Знание этих чисел экономит больше времени, чем любая оптимизация кода.

Глубина пагинации: 2000 записей на запрос

Это самое жёсткое ограничение. page может принимать значения 0–19, per_page — до 100, итого по любому одному набору фильтров возвращается максимум 2000 записей. Запрос «text=Python&area=113» (Python по всей России) почти всегда упирается в этот потолок, и оставшиеся 5–10 тысяч вакансий просто не достать.

Решение здесь не техническое, а методологическое: дробить запрос. Вместо одного «Python по России» делается серия — по регионам, по дате публикации, по уровню зарплаты, по профессиональным ролям. Каждая узкая выборка возвращает свои 2000, и в сумме покрытие получается полным. Именно поэтому в зрелых парсерах (например, в hh_research) есть параметр professional_roles и явное разбиение по area — это не оптимизация, а необходимое условие полноты.

Скорость запросов: ~30 RPS теоретически, 5–10 RPS на практике

Документация заявляет около 30 запросов в секунду к API. На длинных дистанциях устойчиво проходит 5–10 RPS, особенно без авторизации. На скрейпинге веб-страниц лимит ниже: по эмпирическим данным открытых проектов, выше 3–5 RPS быстро срабатывает временный бан по IP. Сам автор проекта IlyaKovalev/hh.ru-parser упоминает, что превышение 30 запросов в минуту с одного IP уже может квалифицироваться как DDoS — возможно, цифра спорная, но практика подтверждает: жадный парсер живёт от часа до суток.

Контакты в резюме: 120 в сутки на аккаунт

Это лимит самого сайта, и обходить его технически — значит идти против правил площадки. Просмотр контактов в резюме (телефон, email) ограничен 120 единицами в сутки на один авторизованный аккаунт, что подтверждается, в частности, пресетом Datacol под HH. При интенсивном парсинге аккаунт блокируется, и второй раз уже не разблокируется.

HTTP-коды и что они значат

КодПричинаЧто делать
200, но пустой массивСелекторы устарели, JS не отработал, антибот вернул заглушкуПерейти на API или headless-браузер
400 Bad RequestКривой синтаксис text=, неэкранированные кавычкиУпростить запрос, экранировать символы
403 ForbiddenНужен токен или превышены праваАвторизация через OAuth, проверка scope
429 Too Many RequestsПревышен RPSЧитать заголовок Retry-After, делать экспоненциальный backoff
503 Service UnavailableВременная нагрузка на APIПовтор через 5–10 секунд, не чаще

Особенно коварен случай «200 OK, но списки пустые». Именно с него начинаются почти все вопросы новичков на Хабр Q&A: парсер бодро пишет «Парсинг страницы 1, 200, Парсинг страницы 2, 200…», а на выходе ничего нет. Причина почти всегда одна: контент рендерится JavaScript, а в исходном HTML сидят шаблонные плейсхолдеры вроде {{ salary.diffCompensation }}, которые BeautifulSoup честно вычитывает как текст.

Антибот-защита и техники обхода

HeadHunter применяет многослойную защиту, и в большинстве случаев она настроена не на полный запрет автоматизации (это сломало бы сторонние интеграции), а на отсечение жадных и неаккуратных парсеров.

Что используется на стороне HH

  • Rate limiting — лимит запросов на IP и токен, со снижением приоритета при превышении;
  • User-Agent фильтрация — стоп-лист стандартных python-requests/2.x;
  • Поведенческий анализ — скорость кликов, отсутствие скролла, нечеловеческая регулярность запросов;
  • JS-проверки — часть контента доступна только после рендера;
  • CAPTCHA — при подозрительной активности;
  • Honeypot-элементы — скрытые поля, которые видит только бот;
  • Лимит глубины поиска — те самые 2000 записей.

За 2024–2025 годы HH также начал ограничивать запросы из ряда дата-центров (AWS, GCP), что превратило выбор провайдера прокси из чисто технической задачи в операционно значимую. На первых этапах разработчики используют пул бесплатных прокси, но по мере роста проекта такой подход неизбежно ломается: бесплатные адреса либо в чёрном списке, либо отдают мусор.

Чем отвечает парсер

Самый недооценённый приём — задержки. Случайная пауза в 3–7 секунд между запросами и экспоненциальный backoff при 429 закрывают примерно 70% типичных банов без всякого прокси. Только когда этого недостаточно, в дело идёт ротация IP — и в крайних случаях TOR, как в проекте IlyaKovalev: там парсер генерирует случайное число от 1 до 10, делает ровно столько запросов и меняет выходную ноду TOR. Подход спорный (TOR медленный, и часть нод HH просто блокирует), но архитектурно интересный.

ЗащитаЧто делает HHЧто делает парсер
Rate limit429 при превышенииСлучайные задержки, экспоненциальный backoff, чтение Retry-After
IP-банВременная блокировкаРотация прокси, пул из 10–50 адресов
UA-фильтрВозврат заглушкиПолный набор браузерных заголовков, ротация User-Agent
Поведенческий анализКапча или банHeadless-браузер с случайным таймингом, скроллом и движением мыши
JS-рендерКонтент только после JSSelenium / Playwright; чтение page_source после события load
АвторизацияБез сессии — обрезанный набор полейСохранение cookies, переиспользование сессии

Современные методы детекции — отдельная история. HH пока не использует их в полную силу, но крупные сервисы уже умеют отличать клиента по TLS-fingerprint (JA3, JA4) и порядку HTTP/2-заголовков. Стандартный requests в Python имеет узнаваемый JA3, не совпадающий с Chrome, и поэтому в чувствительных проектах используют библиотеку curl_cffi с импер­сонацией Chrome. Для обхода browser-fingerprinting (Canvas, WebGL, AudioContext) применяют undetected-chromedriver или puppeteer-extra-plugin-stealth. Парсеру HH это пригодится не сегодня, но когда понадобится — оно понадобится сразу и навсегда.

Резюме: правовая красная линия

Здесь начинается зона, где техника отходит на второй план, а на первый выходит закон. Резюме на HH — это персональные данные, и их массовый сбор без согласия субъекта подпадает под ФЗ-152 «О персональных данных». Поправка 152-ФЗ, статья 10.1 (введена в 2021 году), уточнила: даже данные, сделанные субъектом «общедоступными», требуют отдельного согласия на распространение. Просто публикация резюме на hh.ru не равна разрешению копировать его в чужую базу.

Прецедент по теме известный: дело № А40-18827/2017, ВКонтакте против «Дабл Дата». Суд признал, что социальная сеть имеет право защищать базу данных пользователей по статье 1334 ГК РФ — это прямо применимо к HH как к организованной базе резюме. Аналогичные иски от HeadHunter теоретически возможны, и оферта площадки прямо запрещает массовый автоматический сбор.

В цифрах риски выглядят так:

  • Штраф юридическому лицу за нарушение ФЗ-152 — от 300 до 700 тысяч рублей за первое нарушение, до 18 миллионов при повторном массовом;
  • Иск о компенсации со стороны площадки — до 5 миллионов рублей по статье 1334 ГК;
  • Уголовная ответственность для должностных лиц по статье 137 УК РФ — до двух лет лишения свободы.

Это не предотвращает парсинг сам по себе, но превращает его из «упражнения для пятницы» в задачу с реальной юридической ответственностью. Стандартное практическое разделение выглядит так: вакансии можно собирать через API в исследовательских и личных целях. Резюме — нельзя массово ни через API, ни через скрейпинг, кроме случая корпоративного тарифа HH с явным согласием. Если задача — анализ рынка труда без идентификации кандидатов, разумной альтернативой служит государственный портал «Работа России» с открытым API без жёстких ограничений.

Короткое правило. Вакансии — публичные данные о юридических лицах, парсинг разрешён в рамках API. Резюме — персональные данные физических лиц, массовый парсинг запрещён.

Архитектура промышленного парсера

На первых этапах парсер пишется как один скрипт: запрос, цикл, сохранение в CSV. По мере роста проекта такой подход становится неудобным сразу по нескольким причинам: один баг в середине цикла теряет всю выгрузку, повторный запуск ничего не помнит, обновлений нет, ошибки молча проглатываются. Зрелая архитектура решает эти задачи разделением слоёв.

Минимально достаточный пайплайн

  1. Планировщик — Cron, Airflow или встроенные Cron Jobs облачного хостинга. Запускает выгрузку по расписанию.
  2. Воркер — собственно код парсера. Может работать в одном процессе или в кластере, при необходимости через прокси-пул.
  3. Слой валидации — нормализация зарплат (USD/EUR в рубли, gross в net), очистка HTML-тегов из description, дедупликация по id.
  4. Хранилище — для прототипа достаточно SQLite, для продакшна обычно PostgreSQL. Архивные снимки удобно класть в Parquet.
  5. Аналитика — pandas, Jupyter, BI-дашборд. Здесь же живут модели предсказания зарплат и кластеризация навыков.
  6. Доставка — Telegram, email, Excel-выгрузка, REST-эндпоинт для внешних потребителей.

В кейсе hh.ru_pars от DarkDarW хранилище реализовано на SQLite, веб-интерфейс — на Flask, всё запускается через docker-compose. Это рабочий минимум для одного исследователя. На уровне сотен пользователей такой стек ломается, и здесь начинается зона PostgreSQL и асинхронных очередей.

Когда нужен инкрементальный парсинг

Большинство открытых проектов описывают полный сбор: «выкачали всё, посчитали статистику». В реальной задаче гораздо чаще нужен инкремент: «что появилось со вчера». В API HH есть параметры date_from и date_to, и поле published_at у каждой вакансии — но архитектура incremental loader редко разбирается в туториалах. Минимум, который должен быть: таблица «последний обработанный published_at на запрос», и запрос идёт от этой точки. Такой подход снимает 90% нагрузки и позволяет держать актуальную базу при минимальном RPS.

Очередь задач и FOR UPDATE SKIP LOCKED

Если парсер не только собирает данные, но и совершает действия (отклики на вакансии, отправка писем) — нужна полноценная очередь. Хороший пример — кейс «Авроры»: бот в Telegram при нажатии «Старт» находит 100 вакансий, на каждую генерируется письмо через LLM (5–10 секунд) и делается POST в /negotiations (1–2 секунды). Синхронный отклик блокирует event loop полностью.

Решение через PostgreSQL-очередь использует ключевую SQL-конструкцию:

UPDATE application_queue SET status = 'processing'
WHERE id = (
    SELECT id FROM application_queue
    WHERE status = 'pending'
    ORDER BY created_at
    LIMIT 1 FOR UPDATE SKIP LOCKED
)
RETURNING *;

Транзакция атомарно находит свободную задачу, блокирует её, а если запись уже занята другим воркером — пропускает и берёт следующую. Это позволяет нескольким инстансам бота работать с одной таблицей без конфликтов и без сложной координации. Даже если воркер упадёт посреди обработки, статус останется в processing, и при перезапуске задача восстановится из БД, а не пропадёт.

Кейсы реальных проектов

hh_research: исследование рынка через API

Open-source проект Александра Капитанова — это полноценный исследовательский тулкит на Python. Архитектура простая: настройка через JSON-конфиг (text, area, per_page, professional_roles), многопоточный сбор через API, нормализация зарплат с пересчётом USD/EUR в рубли и gross в net, сохранение в DataFrame и далее в CSV. Поверх этого — статистика по медиане, средней и распределению зарплат, частотный анализ ключевых навыков и предсказание зарплаты для вакансий, где она не указана. Проект собрал около 120 звёзд на GitHub — ориентир по качеству и зрелости кода.

gaisin/hh_parser: частотный анализ навыков

Маленький демо-парсер, написанный ещё в 2017 году, но именно он показывает, насколько быстро устаревают данные. На запрос «python» по всей России (area=113) в выборке из 2000 вакансий чаще всего встречались: python (493), linux (248), git (157), sql (153), postgresql (132), javascript (114), java (106), django framework (97), mysql (93), c++ (80). Сегодня в топе появился бы Kubernetes, Golang, MLOps, LLM — навыки, которых в 2017 году в выдаче практически не было. Это иллюстрирует data drift: модель, обученная на старых частотах, к актуальному рынку относится плохо.

Friztutu/hh.ru_scraper: автоотчёты с графиками

Проект собирает CSV, текстовый автогенерируемый отчёт и графики распределения зарплат. По данным репозитория, парсинг 100 страниц (около 2000 вакансий) занимает примерно час — это разумный ориентир для скрейпинга через requests без прокси и многопоточности. Если поднять конкурентность до 10 потоков и переехать на API, ту же задачу реально решить за 3–5 минут.

«Аврора»: гибридный поиск + LLM-отклики

Telegram-бот, описанный в материале на Хабре, объединяет три нетипичных для HH-парсеров приёма: гибридный поиск через similar_vacancies с переключением на /vacancies, генерацию персонализированных писем через Gemini и асинхронную очередь на PostgreSQL. Конечный автомат настройки реализован через ConversationHandler из python-telegram-bot, и автор отдельно подчёркивает, что его отладка заняла больше времени, чем сам поиск. Это типично для таких систем: техника парсинга обычно проще, чем UX и обработка ошибок.

IlyaKovalev/hh.ru-parser: парадокс TOR

Незавершённый проект 2019 года, интересный самим подходом: запросы идут через torsocks, причём с дополнительной случайностью — скрипт выбирает случайное число от 1 до 10, делает ровно столько запросов и меняет выходную ноду. Архитектурно это интересный вариант обхода IP-бана без коммерческих прокси, но на практике скорость TOR делает его пригодным только для небольших исследовательских выгрузок. Для парсинга 100 тысяч вакансий через TOR уйдут сутки.

Готовые сервисы и no-code инструменты

Не каждый, кому нужны данные с HH, готов писать код. Для таких случаев существует целый сегмент готовых решений — от настольных парсеров до полностью управляемых сервисов с API доступа к выгрузкам. Сравнительная таблица помогает выбрать по типу задачи:

СервисТипЧто отдаётОсобенностиURL
hh_researchOpen-source библиотека (Python)CSV с вакансиями, статистика, графикиБесплатно, требует настройки и Pythongithub.com/hukenovs/hh_research
Авто-Парсер.руSaaS под ключЕжедневные выгрузки в CSV/Excel + REST API с ключомРазделитель CSV — вертикальная черта, обновление после 06:00 МСКauto-parser.ru/parser_hh_ru
Datacol (web-data-extractor)Настольное приложениеCSV, Excel, выгрузка в WordPress, Joomla, DLEДемо хранит первые 25 записей; для резюме нужна авторизация в браузереweb-data-extractor.net/parser-vakansij-hh-ru
ParsingMasterЗаказной парсингДанные в любом формате под задачу клиентаУслуга «под ключ», интеграция в CRM/CMSparsingmaster.com/parsing-hh
Octoparse / ParseHubNo-code парсерыСтруктурированные данные через визуальный конструкторПодходят для разовых задач, плохо масштабируются на APIoctoparse.com / parsehub.com

Выбор между этими вариантами обычно сводится к трём вопросам. Нужны ли данные на регулярной основе? Если да — SaaS экономит больше, чем стоит. Допустимо ли держать инфраструктуру самому? Если нет — заказной парсинг или подписка предпочтительнее. Какая часть задачи приходится на аналитику, а какая на сбор? Если 80% работы — это анализ, имеет смысл купить готовую выгрузку и сосредоточиться на интересном; если 80% — это автоматизация бизнес-процесса, лучше писать своё, потому что любой готовый сервис рано или поздно упрётся в специфические требования.

Типичные ошибки и как их избежать

Большая часть граблей в парсинге HH повторяется из проекта в проект. Вот короткий перечень того, что чаще всего ломает выгрузки.

  1. Старт со скрейпинга вместо API. Самая частая и дорогая ошибка. На API HH хорошая документация и предсказуемое поведение; HTML меняется каждые 2–3 месяца, и парсер на BeautifulSoup тоже придётся менять каждые 2–3 месяца. Скрейпер пишется быстрее, но обходится дороже.
  2. Игнорирование лимита 2000 записей. Парсер «доходит» до 19-й страницы, видит пустой ответ и думает, что собрал всё. На самом деле ушло полторы тысячи вакансий из десяти. Решается дроблением запроса по фильтрам.
  3. Отсутствие задержек. Запрос в цикле без sleep почти гарантированно даёт 429 в течение часа. Случайные паузы в 1–3 секунды и экспоненциальный backoff после первого 429 закрывают вопрос на 90%.
  4. Парсинг рендеримой страницы через requests. Классический симптом — ответ 200, в коде {{ salary.diffCompensation }}, на странице ничего нет. Лечится либо переездом на API, либо переходом на Selenium/Playwright.
  5. Хранение в одном плоском CSV. На 10 тысячах записей удобно, на 100 тысячах уже мучительно, на миллионе — невозможно. SQLite спасает до миллиона, после нужен PostgreSQL.
  6. Отсутствие нормализации зарплат. В выгрузке смешаны RUB, USD, EUR, gross и net, и медианная зарплата получается бессмысленной. Все значения нужно приводить к рублям после НДФЛ до загрузки в аналитику. Ошибка в нормализации одного поля искажает все производные метрики.
  7. Отсутствие дедупликации. Одна вакансия может быть спарсена дважды, если запросы пересекаются по фильтрам. id вакансии — естественный первичный ключ, и UNIQUE-индекс по нему предотвращает дубли на уровне БД.
  8. «Вечные» вакансии. HR-отделы переоткрывают одну и ту же позицию каждые 30 дней, чтобы держать её в топе выдачи. Получается, что новых вакансий с компании 50, а реальных — 5. Простой эвристический фильтр (одинаковое название и описание у одного работодателя) убирает большую часть шума.

Короткий чек-лист перед запуском в продакшн. Запрос разбит по фильтрам? Зарплаты нормализованы в одну валюту и один режим (gross/net)? Включён бэкофф на 429? Есть UNIQUE по vacancy.id? Есть инкрементальный парсинг по published_at? Если хотя бы на один пункт ответ «нет» — продакшн преждевременен.

Парсинг HH хорошо иллюстрирует общий принцип: техническая часть редко бывает самым сложным. Самое сложное — это не «как достать данные», а «как сделать так, чтобы выгрузка не превратилась в постоянный источник тушения пожаров». Грамотная архитектура и опора на официальный API превращают парсер из расходного скрипта в работающий инструмент анализа рынка труда — и именно в этом разница между прототипом и продакшном.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *