Pull to refresh

Анализ вакансий HeadHunter

Reading time 6 min
Views 26K


Однажды мне стало интересно, а что если попробовать проанализировать вакансии и составить по ним некоторые топы. Узнать кому больше всех платят, кто наиболее востребован и много чего еще.


В качестве источника данных я использовала всем известный HeadHunter. Были собраны и обработаны вакансии за май этого года. Только за месяц, потому что API не позволяет получить больше.


Сбор данных


HeadHunter API имеет отличную документацию, которая расположена в репозитории. Запросы должны осуществляться на домен https://api.hh.ru/ с установленным User-Agent, желательно вида название_приложения/версия_приложения (емейл_для_связи) (иногда срабатывают другие варианты User-Agent, но если серверу что-то не понравится, он вернет ошибку).


Логика сбора очень простая, поэтому я реализовала её на bash с использованием cURL и jq. Однако хочу поделиться несколькими нюансами.


Пагинация


Для поиска вакансий по различным параметрам имеется эндпоинт GET /vacancies.


curl -A 'irenica (https://irenica.com/)' 'https://api.hh.ru/vacancies'

Результаты поиска будут разбиты на страницы, за размер которых отвечает параметр per_page (20 по умолчанию и 100 максимум). Выбрать конкретную страницу можно, указав параметр page (нумерация начинается с 0).


В поле pages служебной информации, возвращаемой вместе с вакансиями, будет указано общее количество страниц результата.


С помощью этого можно легко перебрать все страницы:


declare -i i=0
while true; do
  declare url="https://api.hh.ru/vacancies?per_page=100&page=$i"
  declare page="$(curl -A 'irenica (https://irenica.com/)' "$url")"
  # обрабатываем $page

  ((i++))
  declare -i totalCount=$(echo "$page" | jq '.pages')
  if ((i >= totalCount)); then
      break
  fi
done

Полные данные вакансии


Однако, результаты поиска содержат лишь часть данных о вакансиях. Чтобы получить все, нужно совершить отдельный запрос на эндпоинт вида GET /vacancies/id_вакансии.


Частичные данные о вакансиях находятся в поле items результатов поиска. Сначала соберем из них ID вакансий:


declare vacanciesIds="$(echo "$page" | jq -r '.items[].id')"

Затем запросим полную информацию о соответствующих вакансиях по отдельности:


for vacancyId in $vacanciesIds; do
  declare url="https://api.hh.ru/vacancies/$vacancyId"
  declare vacancy="$(curl -A 'irenica (https://irenica.com/)' "$url")"
  # обрабатываем $vacancy
done

Обход лимита поиска


У HeadHunter API есть одна особенность — сколько бы ни было найдено, возвращено будет максимум 2000. При этом реальное найденное количество так же возвращается в поле found результатов поиска. Благодаря этому, можно точно узнать, получил ли ты все запрошенные данные, или есть потери.


Чтобы обойти данное ограничение, я придумала следующее. При поиске можно задать отрезок времени, когда публиковались интересующие вакансии (через параметры date_from и date_to, которые принимают дату в формате ISO 8601). Можно взять маленький промежуток и перебрать такими кусочками все результаты: ведь чем меньше интервал времени, тем меньше вакансий успели за него опубликовать.


Стоит обратить внимание, что возвращаются вакансии, опубликованные только за последний месяц. Поэтому нет смысла задавать диапазон больше.


Для перебора отрезков времени последнее лучше всего представлять в виде Unix time:


declare -i startTime=$(date -d '-1 month' +%s)
declare -i endTime=$(date -d now +%s)
while ((startTime <= endTime)); do
  declare -i intervalEnd=$((startTime + 60*60))
  declare startTimeIso="$(date -d @$startTime +%FT%T)"
  declare intervalEndIso="$(date -d @$intervalEnd +%FT%T)"

  # ...
  declare url="https://api.hh.ru/vacancies?per_page=100&page=$i&date_from=$startTimeIso&date_to=$intervalEndIso"
  # ...

  startTime=$intervalEnd
done

Обработка зарплаты


Для сбора статистики требовалось группировать вакансии по определённым признакам. На bash это делать было уже проблематично, поэтому я воспользовалась Python.


Логика сбора ничего особенного собой не представляет — накопление данных в ассоциативном массиве, сортировка и вывод в CSV. Однако, снова несколько нюансов.


Зарплатная вилка


Нужно обратить внимание, что зарплата представлена в виде двух чисел — минимальной и максимальной, причем любое из них может отсутствовать.


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


salary = None
if vacancy['salary']:
    if vacancy['salary']['to']:
        salary = vacancy['salary']['to']
    if vacancy['salary']['from']:
        salary = vacancy['salary']['from']

Курсы валют


Зарплата в вакансии может быть указана в разных валютах, а они — иметь разный курс. HeadHunter API имеет эндпоинт GET /dictionaries, содержащий все необходимые предопределенные значения. Курсы валют представлены в поле currency. Для удобства работы будет лучше поместить их список в ассоциативный массив, где ключом будет буквенный код валюты:


currencies = {}
dictionaries = requests.get('https://api.hh.ru/dictionaries').json()
for currency in dictionaries['currency']:
    currencies[currency['code']] = currency['rate']

Теперь, во время обработки, будет легко сконвертировать все зарплаты в одну валюту:


salary /= currencies[vacancy['salary']['currency']]

Учёт НДФЛ


В некоторых вакансиях зарплата указана до выплаты НДФЛ, в некоторых — после. О конкретном варианте говорит поле gross: оно равно true в первом случае и false — во втором.


Я решила перевести все зарплаты в вариант после уплаты налога:


if vacancy['salary']['gross']:
    salary -= salary * 0.13

Анализ результатов


А теперь самое время показать цифры.


Удалённая работа


Наверное, многие из тех, кто читает этот пост, хотели бы работать на удалёнке. Но как видим, работа из дома у нас в стране пока не очень котируется. Зарплата сильно ниже, количество вакансий ощутимо меньше. А стало быть меньше и возможность выбора для соискателя.


И это достаточно странно, ибо во многих профессиях и многих фирмах (по специфике задач) присутствие человека в офисе совсем не нужно. Но это вечный спор.


Name Salary, average Salary, minimum Salary, maximum Number
Домашний персонал 112536 10977 130000 19
Информационные технологии, интернет, телеком 55225 1000 300000 2828
Высший менеджмент 47687 9474 100000 23
Добыча сырья 46579 20000 90898 80
Инсталляция и сервис 45439 11874 69600 9
Государственная служба, некоммерческие организации 44911 20000 90000 19
Рабочий персонал 44218 9499 67860 37
Производство 42388 2372 100000 236
Строительство, недвижимость 39896 70 110000 329
Транспорт, логистика 37662 9490 100000 223


Соискатели с инвалидностью


Однако, есть ещё более малочисленная категория вакансий — для людей с инвалидностью. И уж это совсем нелогично — пусть работодатели не хотят удалёнщиков, но из тех, кто готов на такое, почему так мало, кто думает о людях с ограниченными возможностями? Если вам всё равно, что человек в трёх часовых поясах, какая вам разница, в состоянии ли он, например, ходить?


Возможно, многие из вас знакомы с людьми, имеющими инвалидность. Я тоже, и мне стало интересно, насколько сложно им найти работу, и на что вообще они могут рассчитывать.


Name Salary, average Salary, minimum Salary, maximum Number
Государственная служба, некоммерческие организации 69675 8700 90000 8
Высший менеджмент 48705 30000 82425 15
Информационные технологии, интернет, телеком 45321 4350 200000 1050
Наука, образование 45056 3158 90000 376
Закупки 43591 15000 80000 9
Строительство, недвижимость 42148 22 250000 210
Производство 40969 10000 130500 189
Бухгалтерия, управленческий учет, финансы предприятия 36387 2610 113100 125
Юристы 34308 2610 160000 131
Безопасность 33414 22 90000 178


Студенты


Все мы с чего-то начинаем, а именно с поиска работы, не имея никакого опыта. Решила оценить ситуацию с позициями, открытыми для таких кандидатов.


Количество вакансий обнадёживает на быстрое трудоустройство. И не знаю насколько реально получить максимальный оклад, но на средние цифры даже можно как-то жить.


Name Salary, average Salary, minimum Salary, maximum Number
Консультирование 62601 1500 221850 2504
Строительство, недвижимость 55855 20 949989 6455
Высший менеджмент 50826 11310 400000 111
Добыча сырья 38192 8000 100000 328
Безопасность 34617 3954 100000 5844
Медицина, фармацевтика 34475 450 200000 11776
Транспорт, логистика 33600 500 150000 8000
Наука, образование 31426 1100 124510 1660
Продажи 30444 1 350000 52566
Инсталляция и сервис 30360 8264 80000 381


Общий топ


А теперь самое интересное: кому же платят больше всех? Отсортировала все найденные вакансии без каких-либо фильтров.


Конечно же это высший менеджмент. Кто бы сомневался.


Любопытный факт: если обратить внимание на среднюю зарплату во всех таблицах, то можно заметить, что она не так уж сильно отличается.


Name Salary, average Salary, minimum Salary, maximum Number
Высший менеджмент 78789 150 2000000 2408
Добыча сырья 61699 8000 180000 2302
Консультирование 59797 1500 500000 3762
Информационные технологии, интернет, телеком 52777 26 684804 25900
Строительство, недвижимость 48587 20 949989 33229
Производство 42007 1 261000 27269
Рабочий персонал 41203 25 200000 43079
Автомобильный бизнес 38555 20 824254 9269
Инсталляция и сервис 38412 25 180000 2390
Закупки 37846 50 261000 2658


Уборщицы


А вот и самый простой путь: зачем учиться 5 лет, если можно просто помыть офис? Ниже результат фильтрации топа вакансий по запросу «уборщ*».


Что, если устроиться в несколько контор и приходить вечерком на пару часов для уборки? Так можно довольно роскошно жить. Будем считать это лайфхаком.


Name Salary, average Salary, minimum Salary, maximum Number
Высший менеджмент 63000 40000 87000 8
Маркетинг, реклама, PR 50000 50000 50000 6
Добыча сырья 45000 45000 45000 3
Управление персоналом, тренинги 33246 7908 87000 58
Бухгалтерия, управленческий учет, финансы предприятия 32000 30000 35000 10
Безопасность 31507 20000 70000 6
Продажи 29696 4737 55000 159
Строительство, недвижимость 29024 413 80000 73
Транспорт, логистика 24987 10990 45000 26
Автомобильный бизнес 24465 7124 45000 61


Топ по городам


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


Name Number
Москва 31137
Санкт-Петербург 11745
Минск 7608
Алматы 4386
Киев 3398
Екатеринбург 3182
Новосибирск 3097
Казань 3066
Уфа 2980
Нижний Новгород 2876


Репозиторий


Весь код из статьи, с улучшениями и инструкцией, доступен в репозитории.

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+16
Comments 15
Comments Comments 15

Articles