Программирование, Веб-парсинг

Еда в окрестностях Джакарты (часть 1): Zomato Web Scraping с использованием селена

Хотите знать, какие продукты (и напитки) может предложить Большой дуриан? Давайте почистим это селеном!

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

Джакарта вступает в восьмой месяц пандемии COVID-19, и судя по нынешнему положению дел, ситуация не улучшается. Наше правительство периодически вводит в город социальные ограничения.

Людям предлагается сидеть дома и работать из дома, производство второстепенных товаров рекомендуется временно закрыть, и да, это включая рестораны или десертные салоны, которые вам нравятся!

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

Готовить или не готовить - вот в чем вопрос.

Когда включается регулирование социальных ограничений, "есть вне дома" выбрасывается в окно. Мы можем покупать ингредиенты, а затем готовить сами, поэтому выбираем вариант «в готовить».

Вариант «не готовить»: покупайте еду на вынос или, что еще безопаснее, заказывайте еду в Интернете. Как житель Джакарта, который изо всех сил старается не способствовать новым случаям COVID в этом городе, я иногда заказываю еду через Интернет, благодаря развитию технологий доставки еды в Индонезии, GoFood или GrabFood (аналогично Uber Eats)

Я обнаружил, что постоянно заказываю одни и те же продукты из одних и тех же ресторанов через GoFood. Когда мне захотелось курицы, я обнаружил, что заказываю еду в KFC и McDonald (ДА, McD предлагает курицу в Индонезии). Когда я жаждал Сото, я обнаружил, что заказываю еду в Сото Кудус Блок М Тебет и т. Д.

В Джакарте буквально тысячи ресторанов, и в течение 8 месяцев пандемии я заказывал только в менее чем 20 ресторанах Джакарты.

Осознавая это, а также читая статью Регита Х. Закиа Еда вокруг меня: извлечение данных из карт Google с помощью Python и Google Colab , меня привлекает подробное описание того, что она сделала в большем масштабе.

Сколько видов еды на самом деле может предложить этот город? Сколько ресторанов предлагают мои любимые индонезийские блюда - Сото и Ренданг? Так много вопросов.

Так что на этот раз я собираюсь извлекать данные об индустрии общественного питания не из Google, а из Zomato. Почему? Здесь Jakartan Foodies ставят свои оценки и пишут свои отзывы. Это надежный источник ссылок на продукты питания.

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

Какие данные мы собираемся очищать?

Но прежде всего, поскольку в Zomato много данных, мы должны перечислить данные, которые нам нужны для нашего небольшого исследования. Проверив страницу Zomato, решил, что соскребу:

Со страницы поиска:

  1. Веб-адреса всех ресторанов в районе Джакарты
  2. Веб-адреса ресторанов, обслуживающих службу доставки в районе Джакарты

Со страницы (Индивидуального) ресторана:

  1. Название ресторана
  2. Тип ресторана
  3. Ресторанная зона
  4. Рейтинг ресторана
  5. Обзор ресторана
  6. Средняя цена ресторана на двоих
  7. Адрес ресторана
  8. Дополнительная информация / услуги ресторана
  9. Ресторан Широта и Долгота

Базовая подготовка

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

В этот раз я буду использовать Google Chrome, а драйвер браузера вы можете скачать здесь.

Осмотрите страницу поиска

Список веб-адресов всех ресторанов в районе Джакарты

Selenium - очень удобная библиотека для поиска HTML-элементов с использованием различных средств, таких как id, имя класса, имя тега, текст ссылки , XPath или селектор CSS. В этом руководстве я также расскажу о различных проблемах, обнаруженных мной при сканировании Zomato Page, а затем объясню, как их решать.

Давайте посмотрим на страницу поиска ниже. Если мы прикрепим наше местоположение к Джакарте, в настоящее время есть 1002 страницы, содержащие около 15 ресторанов на каждой странице. Это может означать, что в Джакарте около 15 000 ресторанов! Вау, это невероятно!

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

Перед написанием кода Python мы должны узнать разницу между двумя инструментами «Найти элемент» Selenium:

  • Найти элемент. Найдите отдельный элемент, указав конкретный локатор HTML-элемента.
  • Найти элементы: найдите список элементов, указав общий локатор HTML-элементов среди элементов, которые вы хотите найти.

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

Мы должны проверить HTML-код страницы, а затем найти общий HTML-элемент среди всех веб-адресов на каждой странице поиска.

Проверив все веб-адреса на этой странице поиска, я пришел к выводу, что одним из распространенных HTML-элементов веб-адреса является их имя класса.

Теперь мы можем получить список Selenium Web Elements, написав такой код:

url_elt = driver.find_elements_by_class_name("result-title")

Кажется, достаточно просто, но наша цель - адрес веб-сайта, верно? Что ж, нам просто нужно написать дополнительный код для извлечения атрибута (href) страницы URL из каждого веб-элемента путем циклического просмотра списка.

С помощью приведенного выше кода мы сможем создать список веб-адресов. Теперь давайте объединим его с кодами для просмотра страниц поиска в Zomato, всего 1002 страницы.

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

Добавляя 2 простых кода печати, мы можем получать информативное уведомление каждый раз, когда мы успешно просматриваем страницы поиска.

Не забудьте преобразовать список в Pandas DataFrame, чтобы мы могли аккуратно упорядочить данные.

out_df = pd.DataFrame(out_lst, columns=['Website'])

Исход:

Список веб-адресов всех ресторанов, обслуживающих доставку в районе Джакарты

Затем мы сделаем то же самое, чтобы получить список ресторанов, обслуживающих в районе Джакарты. Единственная разница - это URL страницы поиска и количество страниц поиска, которые мы хотим просмотреть.

Исход:

Удалить повторяющийся веб-адрес

Теперь у нас есть 14886 ресторанов в районе Джакарты, и 3306 из них обслуживают службу доставки. Прежде чем углубляться в парсинг веб-страниц, мы должны убедиться, что у нас нет повторяющихся записей, потому что результат на странице поиска иногда содержит повторяющиеся записи.

Мы можем легко обнаружить это, используя очень простой метод pandas, продублированный.

# Observe whether we have duplicate websites or not
out_df[out_df.duplicated(['Website'], keep='first')]

Используя приведенный выше код, мы увидим список веб-адресов, которые дублируются в DataFrame, за исключением первой записи из дублированных.

Давайте создадим новый DataFrame без повторяющихся значений как для DataFrame для всех ресторанов, так и для DataFrame для ресторанов с доставкой.

# Make A New DataFrame - without duplicated values
out_df_nd = out_df[~out_df.duplicated(['Website'], keep='first')]
outdlv_df_nd = outdlv_df[~outdlv_df.duplicated(['Website'], keep='first')]

Изучите отдельную страницу ресторана

Теперь у нас есть уникальные веб-адреса ~ 14500 ресторанов по всей Джакарте. Используя это, мы можем перебирать каждый веб-адрес и извлекать нужную нам информацию. Продолжаем писать код!

Название ресторана

Из рисунка выше довольно очевидно, что название ресторана помечено тегом h1 в HTML-коде. Раньше мы находили элементы в HTML-коде, используя имя класса, теперь мы будем искать его, используя имя тега.

name_anchor = driver.find_element_by_tag_name('h1')

Никогда не забывайте, что элемент find возвращает Selenium Web Element, поэтому мы должны извлечь его дальше, чтобы получить нужные нам данные. В нашем случае это можно сделать с помощью этого кода.

name = name_anchor.text

Вот полный код для очистки названия ресторана:

После очистки страниц ресторана в течение нескольких секунд или нескольких часов, возможно, ваша программа остановится, показывая сообщение об ошибке, потому что не удалось очистить какой-либо элемент страницы h1. В случае с Zomato есть такие страницы:

Если наш браузер перейдет на эту страницу, программа остановится, потому что не сможет очистить свой h1. Мы можем легко решить эту проблему, используя другую функцию из библиотеки Selenium: NoSuchElementException. Если мы не можем найти нужный веб-элемент в сочетании с оператором if, мы можем перенаправить программу, чтобы просто передать его. Во-первых, мы должны импортировать эту функцию в начало кода.

from selenium.common.exceptions import NoSuchElementException

После этого мы будем использовать оператор try-except для реализации нашей логики в программе. Если мы не найдем ни одного элемента h1, просто напишите «Ошибка 404» в Название ресторана, а затем перейдите на следующую страницу.

Кроме того, как и раньше, давайте напишем несколько кодов print, чтобы показать, как выполняется парсинг веб-страниц.

Исход:

Тип ресторана

Следующим шагом мы хотим проанализировать тип каждого ресторана в Джакарте.

Как и веб-адрес, мы будем использовать поиск элементов, потому что есть несколько элементов, которые мы хотим очистить. Вопрос в том, какой локатор лучше использовать для поиска этого элемента? Если присмотреться, то имя тега (div) недостаточно уникально, в то время как имя класса (sc-jxGEy0 ) может отличаться на страницах некоторых ресторанов. Вот почему у нас не было этих двух локаторов.

Здесь очень удобен XPath. Что же такое XPath? Это означает XML Path Language, который мы можем использовать, чтобы найти элемент, который мы хотим очистить, потому что структура страницы ресторана Zomato в основном такая же.

Как получить этот полезный XPath? Просто щелкните правой кнопкой мыши нужный HTML-код и выберите Копировать - ›XPath.

Теперь, когда у нас есть XPath, просто вставьте его в свою программу кодирования и добавьте этот код. Обратите внимание, что, как и rest_name, мы должны создать пустой список: rest_type вверху.

Район и адрес ресторана

Затем нам нужно очистить Зона ресторана и Адрес. Это проще, потому что нам нужно очистить только 1 элемент, и, как и раньше, мы будем очищать его с помощью XPath.

Обзор и рейтинг ресторана

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

Средняя цена ресторана на двоих

До сих пор метод XPath весьма удобен для очистки необходимых нам данных. Теперь для этих данных мы должны изменить наш «найти элемент по XPath» с помощью пары операторов if-else. Причина, по которой для этих конкретных данных местоположение на каждой странице ресторана различается.

На изображении выше «Средняя стоимость» находится под «Популярные блюда» и «Люди говорят, что это место известно». На большинстве страниц Zomato эти две части информации не отображаются, поэтому расположение средней стоимости отличается на страницах, на которых есть эти две части.

Мы будем использовать функцию среза строки, чтобы проверить, начинаются ли очищаемые данные с «Rp» / «Нет» или нет. Если он не начинается с этих двух строк, мы очистим другой XPath.

Давайте реализуем эту логику в нашем коде.

Дополнительная информация о ресторане

Мы скопировали отдельные веб-элементы с помощью различных локаторов, несколько веб-элементов с одним и тем же именем класса, несколько веб-элементов внутри XPath, теперь мы научимся очищать еще более уникальная вещь!

Как вы можете видеть на картинке выше, мы хотим очистить всю текстовую информацию, содержащуюся в синем поле. Если мы видим HTML-код слева, информация как бы разбросана по разным кодам.

Что мы сделаем для эффективного очистки: найдем элемент синего поля с помощью XPath, затем, используя результат, мы найдем несколько элементов текста по имени тега p. Вот версия кода этой логики .

Ресторан Широта и Долгота

Этот сбор информации не менее сложен, чем раньше. Чтобы узнать, почему, давайте взглянем.

Как видите, широта и долгота каждого ресторана указаны в веб-адресе карты. Нам не нужно открывать веб-адрес карты, чтобы определить широту и долготу.

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

Еще одно примечание

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

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

Почему? Потому что, в зависимости от скорости вашего интернет-соединения и количества / типа информации, которую мы хотим очистить, нам, возможно, придется подождать, пока браузер полностью загрузит страницу.

import time
# To delay the execution of next coding line
time.sleep(8)

Очистить все данные о ресторанах

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

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

rdf = pd.DataFrame({"Restaurant Name" : rest_name[:], "Restaurant Type" : rest_type[:], "Restaurant Area" : rest_area[:], "Restaurant Rating" : rest_rating[:], "Restaurant Review" : rest_review[:], "Price for 2" : price_for_2[:], "Restaurant Address" : rest_address[:], "Additional Info" : rest_info[:], "Latitude" : rest_lat[:], "Longitude" : rest_long[:]})

Исход:

Вы можете увидеть полный код для очистки отдельной страницы ресторана на Github.

Заключение

Что ж, эта таблица завершает все! Теперь у нас есть данные по отрасли общественного питания Джакарты, которые нам нужны от Zomato. В части 2 я проведу вас через следующий шаг, чтобы завершить эти данные с помощью обратного геокодирования!

Также для вас хочу сказать поздравления! Вы только что научились извлекать данные о ресторанах из Zomato. В процессе вы также научились использовать различные методы для работы с разными видами HTML-кодов.

Кроме того, обратите внимание, что Zomato также может изменить свою структуру HTML, поэтому вам, возможно, придется дополнительно адаптироваться, если есть такие изменения. Каждый веб-сайт индивидуален, поэтому важно адаптировать технику / код для его очистки.

Будьте здоровы во время пандемии, удачного чтения веб-страниц!

Спасибо за прочтение

Я очень надеюсь, что вам понравилась эта статья! Я очень надеюсь, что вы узнали из этого что-то новое и полезное. Если вы действительно это делаете, пожалуйста, похлопайте ниже и оставьте свои мысли ниже, если у вас есть какие-либо отзывы!

Оставайся на связи

  • Следуйте за мной на Medium, чтобы увидеть мою следующую статью.
  • Давай обсудим или хотим сотрудничать? Давайте подключимся к LinkedIn