Всем привет!
Сегодня я расскажу, как наша студенческая команда из СПбПУ разработала систему для сбора и анализа данных о российском IT-рынке труда с помощью платформы hh.ru. Вместо громоздкого монолита мы построили модульное асинхронное приложение на Python, сфокусировавшись на высокой производительности при массовом сборе данных, устойчивости к ошибкам и построении чёткого аналитического конвейера.
Если вам интересно, как эффективно собрать и обработать сотни тысяч записей из публичного API, построить отказоустойчивый пайплайн и сгенерировать аналитические отчёты — устраивайтесь поудобнее. Поехали!
Идея проекта
Рынок IT-вакансий невероятно динамичен. Чтобы понимать реальные тренды — какие навыки востребованы, какие компании больше всего нанимают и какие зарплаты предлагают — нужны данные. Много данных. Нашей целью был переход от домыслов к фактам. Мы решили создать систему, которая могла бы автономно:
Собирать актуальные данные о вакансиях с hh.ru через его публичный API.
Обрабатывать и очищать эти данные, устойчиво справляясь с ограничениями и ошибками API.
Надёжно хранить данные для анализа.
Выполнять сложные агрегации и генерировать визуальные отчёты по ключевым метрикам рынка.
Быть масштабируемой и поддерживаемой, позволяя расширять функциональность.
Мы выбрали модульную, сервис-ориентированную архитектуру на Python, потому что это позволило:
Чётко разделить ответственность (сбор данных, хранение, анализ, визуализация).
Масштабировать компоненты независимо (например, запускать несколько сборщиков данных).
Легко тестировать и отлаживать отдельные части системы.
Сделать проект легковесным и сфокусированным на его основной задаче — обработке данных.
Архитектура решения
Наша система построена по принципу чёткого разделения ответственности. Каждый модуль выполняет одну задачу и общается с другими через простые интерфейсы. Это классический пайплайн ETL (Extract, Transform, Load), адаптированный под особенности hh.ru API.
Вот из каких ключевых компонентов состоит система:
Ядро системы (Python 3.10):
data_collector: Асинхронный сборщик данных, взаимодействующий с API hh.ru.db_manager: Менеджер для работы с базой данных SQLite.analytics: Модуль для анализа данных и генерации графиков.models: Pydantic-модели для валидации и структурирования данных.
Вспомогательные технологии:
httpx: Для асинхронных HTTP-запросов к API.pydantic: Для валидации и парсинга сырых JSON-данных в строгие модели.pandas: Для оперативной агрегации и анализа данных в памяти.matplotlib/seaborn: Для построения всех графиков и визуализаций.SQLite: В качестве ��адёжного и лёгкого хранилища для структурированных данных.
Схема работы данных
Всё начинается с источника данных — публичного API hh.ru (версия 1.0.0). Далее данные проходят несколько этапов:
Сбор и Валидация: Модуль
collectorасинхронно запрашивает вакансии. Полученный JSON проходит строгую проверку через Pydantic-модельVacancy, которая отсеивает некорректные записи и приводит данные к единому формату. Только после этого данные попадают в Базу данных (SQLite).Трансформация и Анализ: Модуль
analyzerзагружает данные из БД вpandas.DataFrame. Здесь происходит основная «магия»: группировка вакансий по ролям, расчёт медианных зарплат, подсчёт частоты навыков и анализ трендов.Визуализация: На основе готовых датафреймов модуль
infographicsгенерирует набор статических, но информативных графиков, готовых для включения в отчёт или дашборд.

Почему именно такой стек?
Асинхронность (
httpx,asyncio): Критически важна для обхода тысяч страниц вакансий без многодневного ожидания.Валидация (
pydantic): Гарантирует целостность данных при сохранении. Мы точно знаем, что в БД попали только данные правильного формата.SQLite: Для одноразового аналитического пайплайна с данными в ~500k записей — это идеальный выбор, работает быстро и надёжно.
pandas: Фактический стандарт для анализа табличных данных в Python. Позволяет за несколько строк кода выполнить сложные агрегации, на которых в SQL ушло бы гораздо больше времени.
Такой подход позволил нам создать не «чёрный ящик», а прозрачную, ремонтопригодную систему, где каждый этап обработки данных можно проверить и доработать.
Технические детали реализации
Давайте заглянем под капот и посмотрим, как реализованы ключевые модули системы. Весь код доступен в нашем открытом репозитории на GitHub.
1. Асинхронный сборщик данных (Collector)
Сердце системы — модуль collector.py, который обходит API hh.ru. Главные задачи: масштаб (сотни тысяч вакансий) и устойчивость (API имеет лимиты и может возвращать ошибки).
Ключевые решения:
Асинхронность: Используем
asyncioиhttpxдля одновременной обработки множества запросов. Это ускорило сбор по сравнению с синхронным подходом.Контроль ошибок: Система обрабатывает HTTP-ошибки 429 (слишком много запросов) и 403 (запрещено), делая паузы и логируя проблемные ID вакансий для повторной попытки.
Валидация на лету: Каждый полученный JSON-объект сразу проходит проверку через Pydantic-модель. Это защищает базу данных от "мусора".
# Упрощённый пример асинхронного запроса с обработкой ошибок
import httpx
from models.vacancy_model import Vacancy
async def fetch_vacancy(session: httpx.AsyncClient, vacancy_id: int) -> Vacancy | None:
url = f"https://api.hh.ru/vacancies/{vacancy_id}"
try:
response = await session.get(url, timeout=10.0)
response.raise_for_status()
# Парсинг и валидация через Pydantic
return Vacancy.parse_obj(response.json())
except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
# Вакансия не найдена - это нормально, пропускаем
return None
logger.warning(f"HTTP error for {vacancy_id}: {e}")
return None2. Модель данных и работа с БД
Pydantic-модель Vacancy — это строгий контракт, описывающий, как должны выглядеть наши данные. Она автоматически приводит типы (например, строку с зарплатой к числу) и отбрасывает лишние поля.
# Упрощённая схема модели Vacancy
from pydantic import BaseModel
class Vacancy(BaseModel):
id: int
name: str
salary_from: Optional[int] = None
salary_to: Optional[int] = None
employer_name: str
key_skills: Optional[str] = None
# ... другие поляРабота с SQLite организована через модуль db_manager. Мы сознательно не использовали ORM, чтобы сохранить полный контроль над запросами и производительностью. Все взаимодействия с БД — через параметризованные SQL-запросы.
3. Анализ и визуализация
После загрузки данных из SQLite в pandas.DataFrame анализ становится простым и выразительным:
# Пример расчёта медианной зарплаты по ролям
import pandas as pd
def calculate_median_salaries(df: pd.DataFrame) -> pd.DataFrame:
# Создаём условную "среднюю" зарплату для анализа
df['salary_mid'] = df[['salary_from', 'salary_to']].mean(axis=1)
# Группируем по профессиональной роли и считаем медиану
median_by_role = df.groupby('professional_role')['salary_mid'].median()
return median_by_role.sort_values(ascending=False).head(15)Модуль infographics.py использует matplotlib и seaborn для превращения этих датафреймов в готовые для публикации графики. Мы создали единый стиль для всех графиков, чтобы визуализация выглядела профессионально.
4. Особенности и "фишки" реализации
Конфигурация через переменные окружения: Все чувствительные настройки (например, задержки между запросами) вынесены в
private_settings.py, который не попадает в Git.Детальное логирование: Каждый этап — от сбора до визуализации — подробно логируется. Это позволило легко отлаживать систему и понимать, на каком этапе возникла проблема.
Производительность
Сбор данных: Обработка ~11 миллионов страниц вакансий для фильтрации 393 тысяч IT-вакансий заняла несколько суток непрерывной работы. Использование асинхронности позволило загружать CPU на 80-90%.
Аналитика: Расчёт всех метрик (топ навыков, зарплаты, распределение) для финального датасета занимает менее 5 минут на обычном ноутбуке.
Память: Пиковое использование памяти не превышает 2-3 ГБ благодаря потоковой обработке и эффективной работе pandas.
Результаты и выводы
После нескольких суток непрерывного сбора данных и их обработки мы получили детальную картину российского IT-рынка на 2025 год. Вот какие инсайты удалось извлечь из 393 тысяч вакансий.
Что увидели в данных?
1. Концентрация рынка: всё решают гиганты
Анализ данных показал впечатляющую концентрацию спроса. На первую десятку компаний-работодателей (Сбер, Ozon, Яндекс, Т1 и другие) приходится свыше 70% всех IT-вакансий. Это говорит о том, что рынок в значительной степени формируется стратегиями найма крупнейших игроков.
2. «Обязательный минимум» разработчика универсален
Самые востребованные технические навыки оказались вполне предсказуемыми, но от этого не менее важными. Абсолютным лидером стал SQL (упоминался в десятках тысяч вакансий), за ним следуют Linux, Python, Git и PostgreSQL. Этот набор можно считать новым «джентльменским набором» IT-специалиста широкого профиля. Среди soft skills лидируют «Клиентская коммуникация» и «Аналитическое мышление».
3. Рынок труда для джуниоров сужается
Несмотря на общий объём вакансий, данные подтвердили тренд, о котором много пишут: сложный вход в IT для новичков. Большинство вакансий (около 96%) — на полную занятость, и более 82% требуют опыта работы от 1 года. Абсолютно доминирует формат полного рабочего дня в офисе (72.3%), в то время как доля полностью удалённых вакансий составляет около 23%.
4. Зарплаты: ожидаемая иерархия
Расчёт медианных зарплат подтвердил логику рынка. На верхушке находятся руководители направлений (250 000+ руб.) и DevOps-инженеры (215 000+ руб.). Специалисты в области Data Science и системные аналитики также входят в топ с зарплатами от 200 000 руб. Интересно, что разрыв между middle и senior-уровнем во многих специализациях не такой большой, как между junior и middle.
Кому может быть полезен этот проект?
Начинающим data-инженерам и дата-сайентистам: Весь код в открытом доступе и может служить учебным примером построения end-to-end ETL/ELT пайплайна на Python.
HR-специалистам и аналитикам рынка: Методология и готовые скрипты могут быть адаптированы для конкурентного анализа или исследования нишевых сегментов рынка.
Студентам и исследователям: Полученный датасет и код для его анализа — отличная основа для научных работ или курсовых проектов в области экономики труда или компьютерных наук.
Исходный код проекта доступен на GitHub
Если у вас есть вопросы по реализации, предложения по улучшению кода или идеи для совместного исследования — добро пожаловать в issues или дискуссии в репозитории!