CoderCastrov logo
CoderCastrov
Ota

Парсинг цен на отели

Парсинг цен на отели
просмотров
7 мин чтение
#Ota

Отельная индустрия продолжает расти в последние 15 лет с момента последнего экономического кризиса. Вместе с ростом конкуренция в отрасли также увеличивается. Для поставщиков отелей стало довольно сложно увеличить выручку. Каждый второй день на рынке появляются новые поставщики, и с этим узкие маржи прибыли для старых поставщиков. Таким образом, стало довольно сложно для OTA (онлайн-туристических агентств) удерживать доход от бронирования.

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

В этом посте мы собираемся спарсить booking.com с помощью Python. По окончании этого руководства вы сможете спарсить цены любого отеля с booking.com, просто указав даты заезда/выезда и уникальный идентификатор отеля.

Требования

Для этого руководства нам понадобится Python версии 3.x, и я предполагаю, что вы уже установили его на свой компьютер. Кроме того, вам нужно установить еще две библиотеки, которые будут использоваться дальше в этом руководстве для парсинга веб-страниц.

Установка

Сначала создайте папку, а затем установите упомянутые выше библиотеки.

mkdir booking
pip install requests 
pip install beautifulsoup4

Внутри этой папки создайте файл Python, в котором будете писать код. Мы собираемся извлечь следующие данные с целевого веб-сайта.

  • Адрес
  • Название
  • Цена
  • Рейтинг
  • Тип комнаты
  • Удобства

Давайте соберем данные с Booking.com

Поскольку все настроено, давайте выполним GET-запрос на целевой веб-сайт и посмотрим, работает ли он.

import requests
from bs4 import BeautifulSoup

l=list()
o={}

headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"}

target_url = "https://www.booking.com/hotel/us/the-lenox.html?checkin=2022-12-28&checkout=2022-12-29&group_adults=2&group_children=0&no_rooms=1&selected_currency=USD"

resp = requests.get(target_url, headers=headers)

print(resp.status_code)

Код довольно простой и не требует объяснений, но позвольте мне немного пояснить. Сначала мы импортировали две библиотеки, которые мы скачали ранее в этом руководстве, затем мы объявили заголовки и целевые URL-адреса. Наконец, мы выполнили GET-запрос на целевой URL-адрес. После печати вы должны увидеть код 200, в противном случае ваш код неверен.

Как парсить данные

Поскольку мы уже решили, какие данные мы собираемся парсить, давайте найдем их местоположение в HTML, используя инструмент "Инспектор" в Chrome.

Для этого урока мы будем использовать методы find() и find_all() из BeautifulSoup, чтобы найти нужные элементы. Структура DOM будет определять, какой метод лучше использовать для каждого элемента.

Извлечение названия и адреса отеля

Давайте откроем инструмент "Инспектор" в Chrome и найдем местоположение названия и адреса.

hotel name and address

Как видите, название отеля можно найти под тегом h2 с классом pp-header__title. Для простоты давайте сначала создадим переменную soup с помощью конструктора BeautifulSoup, а затем извлечем все данные.

soup = BeautifulSoup(resp.text, 'html.parser')

Здесь BS4 будет использовать парсер HTML для преобразования сложного HTML-документа в сложное дерево объектов Python. Теперь давайте используем переменную soup для извлечения названия и адреса.

o["name"] = soup.find("h2",{"class":"pp-header__title"}).text

Аналогичным образом мы извлечем адрес.

hotel address

Адрес объекта находится под тегом span с классом hp_address_subtitle.

o["address"] = soup.find("span",{"class":"hp_address_subtitle"}).text.strip("\n")

Извлечение рейтинга и удобств

Еще раз мы проанализируем и найдем местоположение элемента рейтинга и удобств в DOM.

Рейтинг хранится в теге div с классом d10a6220b4. Мы будем использовать ту же переменную soup для извлечения этого элемента. Следующий код извлечет данные о рейтинге.

o["rating"]=soup.find("div",{"class":"d10a6220b4"}).text

Извлечение удобств немного сложнее. Мы создадим список, в котором будем хранить все элементы HTML удобств. Затем мы запустим цикл for, чтобы перебрать все элементы и сохранить отдельный текст в основном массиве.

Давайте посмотрим, как это можно сделать в два простых шага.

fac=soup.find_all("div",{"class":"important_facility"})

Переменная fac будет содержать все элементы удобств. Теперь давайте извлечем их по одному.

for i in range(0,len(fac)):
    fac_arr.append(fac[i].text.strip("\n"))

Массив fac_arr будет хранить все текстовые значения элементов. Мы успешно смогли извлечь основные удобства.

Извлечение цены и типов комнат

Эта часть является самой сложной частью полного руководства. Структура DOM на booking.com немного сложная и требует тщательного изучения перед извлечением информации о цене и типе комнат.

Здесь тег tbody содержит все данные. Прямо под тегом tbody вы найдете тег tr, который содержит всю информацию из первого столбца.

Затем, спустившись на один уровень вниз, вы найдете несколько тегов td, где можно найти информацию, такую как тип комнаты, цена и т.д.

Сначала давайте найдем все теги tr.

ids = list()

targetId = list()
try:
    tr = soup.find_all("tr")
except:
    tr = None

Одна вещь, которую вы заметите, это то, что у каждого тега tr есть атрибут data-block-id. Давайте соберем все эти идентификаторы в список.

for y in range(0,len(tr)):
    try:
        id = tr[y].get('data-block-id')

    except:
        id = None

    if( id is not None):
        ids.append(id)

Теперь, когда у вас есть все идентификаторы, остальная работа становится немного проще. Мы будем перебирать каждый data-block-id, чтобы извлечь информацию о цене и типе комнаты из их отдельных блоков tr.

for i in range(0,len(ids)):
    
    try:
       allData = soup.find("tr",{"data-block-id":ids[i]})
    except:
       k["room"]=None
       k["price"]=None

Переменная allData будет хранить все HTML-данные для определенного data-block-id.

Теперь мы можем перейти к тегам td, которые можно найти внутри этого тега tr. Давайте сначала извлечем комнаты.

try:
     rooms = allData.find("span",{"class":"hprt-roomtype-icon-link"})
except:
     rooms=None 

Вот здесь интересная часть: когда у вас есть более одного варианта для определенного типа комнаты, вы должны использовать ту же комнату для следующего набора цен в цикле. Позвольте мне объяснить это на картинке.

Здесь у нас три цены для одного типа комнаты. Так что, когда цикл for итерирует, значение переменной rooms будет равно None. Вы можете увидеть это, напечатав его. Поэтому мы будем использовать старое значение комнаты до тех пор, пока не получим новое значение. Надеюсь, вы поняли мою мысль.

if(rooms is not None):
   last_room = rooms.text.replace("\n","")
try:
   k["room"]=rooms.text.replace("\n","")
except:
   k["room"]=last_room

Здесь last_room будет хранить последнее значение комнаты до получения нового значения.

Давайте теперь извлечем цену.

Цена хранится в теге div с классом “bui-price-display__value prco-text-nowrap-helper prco-inline-block-maker-helper prco-f-font-heading”. Давайте используем переменную allData, чтобы найти ее и извлечь текст.

price = allData.find("div",{"class":"bui-price-display__value prco-text-nowrap-helper prco-inline-block-maker-helper prco-f-font-heading"})

k["price"]=price.text.replace("\n","")

Мы наконец-то смогли извлечь все элементы данных, которые нас интересовали.

Полный код

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

В основном, код будет выглядеть так.

import requests
from bs4 import BeautifulSoup

l=list()
g=list()
o={}
k={}
fac=[]
fac_arr=[]
headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"}

target_url = "https://www.booking.com/hotel/us/the-lenox.html?checkin=2022-12-28&checkout=2022-12-29&group_adults=2&group_children=0&no_rooms=1&selected_currency=USD"

resp = requests.get(target_url, headers=headers)

soup = BeautifulSoup(resp.text, 'html.parser')

o["name"]=soup.find("h2",{"class":"pp-header__title"}).text
o["address"]=soup.find("span",{"class":"hp_address_subtitle"}).text.strip("\n")
o["rating"]=soup.find("div",{"class":"d10a6220b4"}).text

fac=soup.find_all("div",{"class":"important_facility"})
for i in range(0,len(fac)):
    fac_arr.append(fac[i].text.strip("\n"))


ids= list()

targetId=list()
try:
    tr = soup.find_all("tr")
except:
    tr = None

for y in range(0,len(tr)):
    try:
        id = tr[y].get('data-block-id')

    except:
        id = None

    if( id is not None):
        ids.append(id)

print("ids are ",len(ids))


for i in range(0,len(ids)):

    try:
        allData = soup.find("tr",{"data-block-id":ids[i]})
        try:
            rooms = allData.find("span",{"class":"hprt-roomtype-icon-link"})
        except:
            rooms=None


        if(rooms is not None):
            last_room = rooms.text.replace("\n","")
        try:
            k["room"]=rooms.text.replace("\n","")
        except:
            k["room"]=last_room

        price = allData.find("div",{"class":"bui-price-display__value prco-text-nowrap-helper prco-inline-block-maker-helper prco-f-font-heading"})
        k["price"]=price.text.replace("\n","")


        
        
        
        g.append(k)
        k={}

    except:
        k["room"]=None
        k["price"]=None


l.append(g)
l.append(o)
l.append(fac_arr)
print(l)

Вывод этого скрипта должен выглядеть так.

Преимущества парсинга Booking.com

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

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

Поскольку на рынке более 200 OTAs, становится гораздо сложнее парсить и сравнивать. Я рекомендую использовать сервисы, такие как hotel search API, чтобы получить все цены на отели в любом городе по всему миру.

Заключение

Очевидно, что парсинг данных отелей выходит за рамки этого примера, и это был всего лишь пример того, как Python может использоваться для парсинга Booking.com в целях сравнения цен. Вы можете использовать Python для парсинга других веб-сайтов, таких как Expedia, Hotels.com и т. д.

Однако, масштабирование парсинга не будет возможным с помощью этого процесса. После некоторого времени booking.com заблокирует ваш IP-адрес, и ваша система сбора данных будет заблокирована навсегда. Для беспроблемного парсинга используйте Web Scraping API, который будет автоматически менять IP-адрес при каждом новом запросе и будет использовать безголовый браузер Chrome, чтобы уменьшить вероятность блокировки.

Дополнительные ресурсы

Вот несколько дополнительных ресурсов, которые могут быть полезными во время вашего путешествия по парсингу веб-сайтов: