Время прочтения: 3 мин.

Довольно часто IT-специалисты для сбора информации занимаются парсингом сайтов. На этот раз перед нами встала задача по парсингу объявлений, размещенных на сайте о продаже объектов недвижимости с целью изучения клиентского опыта и функциональных возможностей сайта. Но при выполнении задания мы столкнулись с тем что, количество выводимых страниц ограничено, при этом всех объявлений значительно больше.

Так, к примеру, по городу Сочи количество объявлений о продаже составило 27 тысяч, а количество объявлений, которое выдает сайт для просмотра, ограничивается 200-ми страницами и объявлений получается всего 2 тысячи.

Так как задача перед нами стояла более глобального масштаба, получить данные по объявлениям по всей России, а это более 2 млн объявлений, пришлось немного поразмыслить. Поиск объявлений на данном сайте происходит с привязкой к геолокации, т.е. каждое объявление имеет точку на карте, а в адресной строке есть явная привязка к координатам:

ne=43.902806%2C40.057251&sw=43.496797%2C39.241516

Поэтому в качестве решения данной задачи решили воспользоваться сервисом  Yandex API, который позволяет использовать картографические данные. Первым делом мы идем на сайт Yandex.Карт и получаем ключ, который нам понадобится для работы с Yandex API, проблем с получением возникнуть не должно.

#Глобальные переменные
step=0.5                                  #Шаг сетки
API='*******-****-****-****-************' #apikey Yandex Map

Присвоим переменной step размер сетки по координатам. Пример 0,5 для города это нормально, а вот для страны это будет много, поэтому шаг необходимо будет уменьшить, например, до 0,3.  В переменную API сохраняем ключ, полученный от Yandex.  Посмотрев документацию, я составил простой запрос на получение данных, в запросе должно быть минимум три параметра, это geocode, apikey, format.

https://geocode-maps.yandex.ru/1.x?geocode=”Сочи”&apikey=5b61a80d-****-****-****-dfda****d641&format=json

Geocode – в этот параметр можем вставить любые данные (город, улица, страна).

Apikey – наш API ключ, полученный по почте.

Format – формат в котором мы хотим получить ответ.

Правильно составленный запрос, вернет данные в json формате, там будет вся информация, которую Yandex знает об объекте. Нам необходима лишь малая ее часть, это точки геолокации, они обозначают края объекта (в данном случае города Сочи).

Точки всего две:

‘lowerCorner’ – обозначает долготу и широту нижней левой точки.

‘upperCorner’ – обозначает долготу и широту правой верхней точки.

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

#Функция получает геопозицию города
def getGeoCode(address):   #Получаем геопозиции города
    global API
    url = 'https://geocode-maps.yandex.ru/1.x?geocode="'+address+'"&apikey='+API+'&format=json'
    resp = requests.get(url)
    resp = resp.json()['response']['GeoObjectCollection']['featureMember']
    if len(resp) > 0:
        return resp[0]['GeoObject']['boundedBy']['Envelope']
    else:
        print('NOT FOUND! --> ' + address)
        return '- -'

При вызове функции с параметром «Сочи» # getGeoCode(«Сочи»), мы получим массив с координатами:

{'lowerCorner': '39.149676 43.395064', 'upperCorner': '40.010388 44.029938'}

И сравнив ее с адресной строкой мы увидим, что есть совпадения

sw=43.395064%2C39.149676&ne=44.029938%2C40.010388

Осталось написать функцию, которая будет делить наш большой прямоугольник на несколько маленьких таким образом, чтобы в поле поиска помещалось менее 2000 объявлений, и мы могли спарсить страницу простым путем.

#Функция принимает на вход координаты из функции getGeoCode
#и делит на сектора и возвращает массив из координат для создания ссылки
def parse_city(Corner):   #Делим город по секторам
    global step           #Шаг сетки
    current_num_sector=0  #Текущий сектор в обработке

    #LON от слова lontitude - долгота
    LON={'lower':float(Corner['lowerCorner'].split(' ')[0]),'upper':float(Corner['upperCorner'].split(' ')[0])}

    #LAT от слова latitude - широта
    LAT={'lower':float(Corner['lowerCorner'].split(' ')[1]),'upper':float(Corner['upperCorner'].split(' ')[1])}

    count_sector=math.ceil(((LON['upper']-LON['lower'])/step)+1)*math.ceil(((LAT['upper']-LAT['lower'])/step)+1)
                               
    print('Кол-во секторов:',count_sector)
    time.sleep(1)
    LON_current=LON['lower']
    LAT_current=LAT['lower']
    result={}
    while current_num_sector<=count_sector:
        result[current_num_sector]={
            'lower_LON':LON_current,
            'upper_LON':(LON_current+step),
            'lower_LAT':LAT_current,
            'upper_LAT':(LAT_current+step)
        }

        LON_current+=step
        current_num_sector+=1
        if LON_current>LON['upper']:
            LAT_current+=step
            LON_current=LON['lower']
#         if current_num_sector>9:
#             return result            
    return result

Данный подход позволил нам справиться с задачей по парсингу 100% объявлений сайта. Надеемся данный «лайфхак» поможет вам при сборе информации.

Ссылка на git-репозиторий