Однажды мне стало интересно, а что если попробовать проанализировать вакансии и составить по ним некоторые топы. Узнать кому больше всех платят, кто наиболее востребован и много чего еще.
В качестве источника данных я использовала всем известный 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 |
Репозиторий
Весь код из статьи, с улучшениями и инструкцией, доступен в репозитории.
Автор: Irenica