Работа с API HeadHunter при помощи python

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Разбираемся на практике с 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) или подождать нашей следующей статьи по данной теме, где мы рассмотрим более сложные примеры.

Источник: https://habr.com/ru/post/666062/


Интересные статьи

Интересные статьи

8 января 2022 года на Хабре вышла статья Александра Клименкова о том, почему мозговой штурм не работает. Это не первая статья такого рода, которая мне попадается, за пределами Хабра особенно интересно...
Формат *.dwg — самый популярный формат хранения электронных чертежей. Зачастую *.dwg-файлы хранятся годами, сохраняются в различных системах, используются разными пользователями и из-за этого могут на...
Мы затеяли рубрику «Где работать в ИТ», чтобы рассказывать вам о том, как устроена внутренняя кухня крутых IT-компаний: как они нанимают новых сотрудников, где и на чём работают, чем...
Самыми горячими темами Moscow Python Conf++ оказались асинхронная разработка, а также сопоставление Python, его лучших практик и инструментария с аналогами из других языков, и его место в ландшаф...