Разбираемся на практике с API HeadHunter при помощи python.
Появилась задача анализа вакансий на рынке труда, и осуществлять ее надо базе HeadHunter. Необходимо получить все вакансии определенной компании по всем городам России. Ознакомившись с документацией по API на github (https://github.com/hhru/api), приступаем к работе.
Для решения задачи используем python. Импортируем необходимые для работы библиотеки:
import requests # Для запросов по API
import json # Для обработки полученных результатов
import time # Для задержки между запросами
import os # Для работы с файлами
import pandas as pd # Для формирования датафрейма с результатами
Стоит разобраться с такой вещью как areas. Всего существует 9 условных зон (стран):
ID страны | Название страны |
5 | Украина |
9 | Азербайджан |
16 | Беларусь |
28 | Грузия |
40 | Казахстан |
48 | Кыргызстан |
97 | Узбекистан |
113 | Россия |
1001 | Другие регионы |
Для каждой страны имеются свои внутренние зоны, которые можно просмотреть через обращение к HH (https://api.hh.ru/areas) с параметром area равным ID страны. К примеру, для России будет найдено свыше 4 тысяч различных городов, сел и других населенных пунктов.
Для получения всех стран со всеми их внутренними зонами воспользуемся следующим фрагментом кода:
def getAreas():
req = requests.get('https://api.hh.ru/areas')
data = req.content.decode()
req.close()
jsObj = json.loads(data)
areas = []
for k in jsObj:
for i in range(len(k['areas'])):
if len(k['areas'][i]['areas']) != 0: # Если у зоны есть внутренние зоны
for j in range(len(k['areas'][i]['areas'])):
areas.append([k['id'],
k['name'],
k['areas'][i]['areas'][j]['id'],
k['areas'][i]['areas'][j]['name']])
else: # Если у зоны нет внутренних зон
areas.append([k['id'],
k['name'],
k['areas'][i]['id'],
k['areas'][i]['name']])
return areas
areas = getAreas()
Если интересует запрос по конкретной зоне (стране), то в параметры request нужно указать ID необходимой зоны, к примеру, для России: {'area': 113}
Вот часть того, что будет храниться в переменной areas:
Следующим шагом стоит найти ID работодателей.
Для этого нужно получить количество работодателей на данный момент и учесть тот факт, что не все порядковые номера существуют и внутренние ограничения API HH на постраничный поиск, глубина которого равна всего 2000 значений.
def getEmployers():
req = requests.get('https://api.hh.ru/employers')
data = req.content.decode()
req.close()
count_of_employers = json.loads(data)['found']
employers = []
i = 0
j = count_of_employers
while i < j:
req = requests.get('https://api.hh.ru/employers/'+str(i+1))
data = req.content.decode()
req.close()
jsObj = json.loads(data)
try:
employers.append([jsObj['id'], jsObj['name']])
i += 1
print([jsObj['id'], jsObj['name']])
except:
i += 1
j += 1
if i%200 == 0:
time.sleep(0.2)
return employers
employers = getEmployers()
Результат того, что будет храниться в переменной employers:
Возьмем для примера 2ГИС с ID 64174 и найдем все вакансии по работодателю в разрезе каждой зоны России (ID 113). В функцию getPage в качестве входных параметров сделаем только номер страницы для постраничного поиска и зону, где будем смотреть вакансии.
def getPage(page, area):
params = {
'employer_id': 3529, # ID 2ГИС
'area': area, # Поиск в зоне
'page': page, # Номер страницы
'per_page': 100 # Кол-во вакансий на 1 странице
}
req = requests.get('https://api.hh.ru/vacancies', params)
data = req.content.decode()
req.close()
return data
Часть кода, где функция getPage и используется:
for area in areas:
for page in range(0, 20):
jsObj = json.loads(getPage(page, area[2]))
if not os.path.exists('./areas/'):
os.makedirs('./areas/')
nextFileName = './areas/{}.json'.format(str(area[2])+'_'+str(area[3])+'_'+str(page))
f = open(nextFileName, mode='w', encoding='utf8')
f.write(json.dumps(jsObj, ensure_ascii=False))
f.close()
if (jsObj['pages'] - page) <= 1:
print('[{0}/{1}] Область: {3} ({2}) - {5} ({4}) Вакансий: {6}'.format(area_list_id+1,
len(areas),
area[0],
area[1],
area[2],
area[3],
jsObj['found']))
break
time.sleep(0.2)
Сохраняем промежуточные результаты в формате json для каждой зоны отдельно, в том числе и для зон, где не найдено ни одной вакансии. Теперь сгруппируем их в один файл:
dt = []
for fl in os.listdir('./areas/'):
f = open('./areas/{}'.format(fl), encoding='utf8')
jsonText = f.read()
f.close()
jsonObj = json.loads(jsonText)
if jsonObj['found'] != 0:
for js in jsonObj['items']:
if js['salary'] != None:
salary_from = js['salary']['from']
salaty_to = js['salary']['to']
else:
salary_from = None
salaty_to = None
if js['address'] != None:
address_raw = js['address']['raw']
else:
address_raw = None
dt.append([
js['id'],
js['premium'],
js['name'],
js['department']['name'],
js['has_test'],
js['response_letter_required'],
js['area']['id'],
js['area']['name'],
salary_from,
salaty_to,
js['type']['name'],
address_raw,
js['response_url'],
js['sort_point_distance'],
js['published_at'],
js['created_at'],
js['archived'],
js['apply_alternate_url'],
js['insider_interview'],
js['url'],
js['alternate_url'],
js['relations'],
js['employer']['id'],
js['employer']['name'],
js['snippet']['requirement'],
js['snippet']['responsibility'],
js['contacts'],
js['schedule']['name'],
js['working_days'],
js['working_time_intervals'],
js['working_time_modes'],
js['accept_temporary']
])
Полученный промежуточный результат сохраняем в DataFrame и сохраняем как файл Excel.
df = pd.DataFrame(dt, columns = [
'id',
'premium',
'name',
'department_name',
'has_test',
'response_letter_required',
'area_id',
'area_name',
'salary_from',
'salaty_to',
'type_name',
'address_raw',
'response_url',
'sort_point_distance',
'published_at',
'created_at',
'archived',
'apply_alternate_url',
'insider_interview',
'url',
'alternate_url',
'relations',
'employer_id',
'employer_name',
'snippet_requirement',
'snippet_responsibility',
'contacts',
'schedule_name',
'working_days',
'working_time_intervals',
'working_time_modes',
'accept_temporary'
])
df.to_excel('result_2gis.xlsx')
Скриншот части конечного результата внутри Excel:
Без особых сложностей, поставленную перед нами задачу выполнили и получили все возможные вакансии по определенному работодателю на разных территориях.
В этом гайде по работе с API HeadHunter рассмотрен базовый функционал API. Тем не менее, для успешного понимая всего функционала и возможностей, вы можете самостоятельно ознакомиться на github HH.ru (https://github.com/hhru/api) или подождать нашей следующей статьи по данной теме, где мы рассмотрим более сложные примеры.