Как очистить данные с веб-сайта на Python
В науке о данных мы всегда говорим «Мусор в мусоре». Если у вас нет данных хорошего качества и количества, скорее всего, вы не получите много полезной информации. Веб-парсинг - один из важных методов автоматического получения сторонних данных. В этой статье я расскажу об основах парсинга веб-страниц и воспользуюсь двумя примерами, чтобы проиллюстрировать 2 различных способа сделать это в Python.
Что такое веб-парсинг
Веб-парсинг - это автоматический способ получить неструктурированные данные с веб-сайта и сохранить их в структурированном формате. Например, если вы хотите проанализировать, какая маска для лица может лучше продаваться в Сингапуре, вы можете очистить всю информацию о маске на веб-сайте электронной коммерции, таком как Lazada.
Можете ли вы очистить со всех веб-сайтов?
Парсинг вызывает всплеск трафика на веб-сайте и может вызвать сбой сервера веб-сайта. Таким образом, не все веб-сайты позволяют пользователям парсить. Как узнать, какие сайты разрешены, а какие нет? Вы можете посмотреть файл robots.txt на веб-сайте. Вы просто помещаете robots.txt после URL, который вы хотите очистить, и вы увидите информацию о том, позволяет ли хост веб-сайта очистить веб-сайт.
Возьмем для примера Google.com
Вы можете видеть, что Google не разрешает парсинг для многих своих дочерних веб-сайтов. Однако он допускает определенные пути, такие как «/ m / finance», и поэтому, если вы хотите собирать информацию о финансах, это полностью законное место для очистки.
Еще одно замечание: это видно из первой строки User-agent. Здесь Google определяет правила для всех пользовательских агентов, но веб-сайт может предоставить определенным пользовательским агентам специальные разрешения, поэтому вы можете захотеть обратиться к информации там.
Как работает парсинг?
Веб-парсинг работает так же, как бот, просматривающий разные страницы веб-сайта и копирующий все содержимое. Когда вы запускаете код, он отправляет запрос на сервер, и данные содержатся в полученном вами ответе. Затем вы анализируете данные ответа и извлекаете нужные части.
Как мы выполняем парсинг веб-страниц?
Хорошо, наконец-то мы здесь. Существует 2 разных подхода к парсингу веб-страниц в зависимости от того, как веб-сайт структурирует свое содержимое.
Подход 1. Если веб-сайт хранит всю свою информацию в интерфейсе HTML, вы можете напрямую использовать код для загрузки содержимого HTML и извлечения полезной информации.
Примерно 5 шагов, как показано ниже:
- Проверьте HTML-код веб-сайта, который вы хотите просканировать.
- Получите доступ к URL-адресу веб-сайта с помощью кода и загрузите все содержимое HTML на странице
- Отформатируйте загруженный контент в удобочитаемый формат
- Извлеките полезную информацию и сохраните ее в структурированном формате
- Для информации, отображаемой на нескольких страницах веб-сайта, вам может потребоваться повторить шаги 2–4, чтобы получить полную информацию.
Плюсы и минусы этого подхода: он простой и понятный. Однако, если интерфейсная структура веб-сайта изменится, вам необходимо соответствующим образом скорректировать код.
Подход 2. Если веб-сайт хранит данные в API, а веб-сайт запрашивает API каждый раз, когда пользователь посещает веб-сайт, вы можете имитировать запрос и напрямую запрашивать данные из API.
Шаги:
- Проверьте сетевой раздел XHR URL-адреса, который вы хотите сканировать.
- Найдите запрос-ответ, который дает вам нужные данные
- В зависимости от типа запроса (отправка или получение), а также заголовка и полезной нагрузки запроса, смоделируйте запрос в своем коде и получите данные из API. Обычно данные, полученные от API, имеют довольно аккуратный формат.
- Извлеките полезную информацию, которая вам нужна
- Для API с ограничением размера запроса вам нужно будет использовать цикл for для многократного получения всех данных.
Плюсы и минусы этого подхода: это определенно предпочтительный подход, если вы можете найти запрос API. Полученные вами данные будут более структурированными и стабильными. Это связано с тем, что по сравнению с интерфейсом веб-сайта у компании меньше шансов изменить свой серверный API. Однако это немного сложнее, чем первый подход, особенно если требуется аутентификация или токен.
Различные инструменты и библиотека для парсинга веб-страниц
Доступно множество различных инструментов для очистки, не требующих программирования. Тем не менее, большинство людей по-прежнему используют библиотеку Python для парсинга веб-страниц, потому что она проста в использовании, а также вы можете найти ответ в ее большом сообществе.
Наиболее часто используемой библиотекой для парсинга веб-страниц в Python является Beautiful Soup, Requests и Selenium.
Beautiful Soup: помогает преобразовать документы HTML или XML в удобочитаемый формат. Это позволяет вам искать различные элементы в документах и помогает быстрее находить необходимую информацию.
Запросы: это модуль Python, в котором вы можете отправлять HTTP-запросы для получения содержимого. Это помогает вам получить доступ к HTML-содержимому веб-сайта или API, отправив запросы Get или Post.
Селен: он широко используется для тестирования веб-сайтов и позволяет автоматизировать различные события (щелчки, прокрутка и т. д.) на веб-сайте для получения желаемых результатов.
Вы можете использовать Requests + Beautiful Soup или Selenium для парсинга веб-страниц. Selenium предпочтительнее, если вам нужно взаимодействовать с веб-сайтом (события JavaScript), а в противном случае я предпочитаю Requests + Beautiful Soup, потому что это быстрее и проще.
Пример парсинга веб-страниц:
Постановка проблемы. Я хочу узнать о местном рынке масок для лица. Меня интересует цена маски для лица онлайн, скидки, рейтинги, количество проданных масок и т. Д.
Пример подхода 1 (загрузка HTML для всех страниц) - Lazada:
Шаг 1. Проверьте веб-сайт (при использовании Chrome щелкните правой кнопкой мыши и выберите "Проверить")
Я вижу, что все данные, которые мне нужны, заключены в элемент HTML с уникальным именем класса.
Шаг 2. Получите доступ к URL-адресу веб-сайта с помощью кода и загрузите все HTML-содержимое страницы
# import library from bs4 import BeautifulSoup import requests # Request to website and download HTML contents url='https://www.lazada.sg/catalog/?_keyori=ss&from=input&q=mask' req=requests.get(url) content=req.text
Я использовал библиотеку запросов для получения данных с веб-сайта. Как видите, пока что у нас есть неструктурированный текст.
Шаг 3. Отформатируйте загруженный контент в удобочитаемый формат
soup=BeautifulSoup(content)
Этот шаг очень простой, и мы просто анализируем неструктурированный текст в Beautiful Soup, и вы получаете то, что показано ниже.
Формат вывода является гораздо более читаемым, и вы можете искать в нем различные элементы или классы HTML.
Шаг 4. Извлеките полезную информацию и сохраните ее в структурированном формате
Этот шаг требует времени, чтобы понять структуру веб-сайта и выяснить, где именно хранятся данные. В случае с Lazada он хранится в разделе Script в формате JSON.
raw=soup.findAll('script')[3].text page=pd.read_json(raw.split("window.pageData=")[1],orient='records') #Store data for item in page.loc['listItems','mods']: brand_name.append(item['brandName']) price.append(item['price']) location.append(item['location']) description.append(ifnull(item['description'],0)) rating_score.append(ifnull(item['ratingScore'],0))
Я создал 5 разных списков для хранения различных полей данных, которые мне нужны. Здесь я использовал цикл for, чтобы просмотреть список элементов в документах JSON внутри. После этого я объединяю 5 столбцов в выходной файл.
#save data into an output output=pd.DataFrame({'brandName':brand_name,'price':price,'location':location,'description':description,'rating score':rating_score})
Шаг 5. Чтобы информация отображалась на нескольких страницах веб-сайта, вам может потребоваться повторить шаги 2–4, чтобы получить полную информацию.
Если вы хотите очистить все данные. Во-первых, вы должны узнать общее количество продавцов. Затем вы должны прокручивать страницы, передавая инкрементные номера страниц, используя полезную нагрузку в URL. Ниже приведен полный код, который я использовал для очистки, и я просматриваю первые 50 страниц, чтобы получить контент на этих страницах.
for i in range(1,50): time.sleep(max(random.gauss(5,1),2)) print('page'+str(i)) payload['page']=i req=requests.get(url,params=payload) content=req.text soup=BeautifulSoup(content) raw=soup.findAll('script')[3].text page=pd.read_json(raw.split("window.pageData=")[1],orient='records') for item in page.loc['listItems','mods']: brand_name.append(item['brandName']) price.append(item['price']) location.append(item['location']) description.append(ifnull(item['description'],0)) rating_score.append(ifnull(item['ratingScore'],0))
Пример подхода 2 (данные запроса напрямую из API) - Ezbuy:
Шаг 1. Проверьте сетевой раздел XHR URL, который вы хотите просканировать, и найдите запрос-ответ, который дает вам нужные данные.
Я вижу из Сети, что вся информация о продукте указана в этом API под названием «Список продуктов по состоянию». Ответ дает мне все данные, которые мне нужны, и это запрос POST.
Шаг 2. В зависимости от типа запроса (отправка или получение), а также заголовка и полезной нагрузки запроса смоделируйте запрос в своем коде и получите данные из API. Обычно данные, полученные от API, имеют довольно удобный формат.
#Define API url url_search='https://sg-en-web-api.ezbuy.sg/api/EzCategory/ListProductsByCondition' #Define header for the post request headers={'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'} #Define payload for the request form data={ "searchCondition": {"categoryId":0,"freeShippingType":0,"filter: [],"keyWords":"mask"}, "limit":100, "offset":0, "language":"en", "dataType":"new" } req=s.post(url_search,headers=headers,json=data)
Здесь я создаю HTTP-запрос POST, используя библиотеку запросов. Для почтовых запросов вам необходимо определить заголовок запроса (настройку запроса) и полезную нагрузку (данные, которые вы отправляете с этим почтовым запросом). Иногда здесь требуется токен или аутентификация, и вам нужно будет сначала запросить токен, прежде чем отправлять свой запрос POST. Здесь нет необходимости извлекать токен и обычно просто следите за тем, что содержится в полезной нагрузке запроса в сети, и определяйте «пользовательский агент» для заголовка.
Еще одна вещь, на которую следует обратить внимание, это то, что внутри полезной нагрузки я указал предел как 100 и смещение как 0, потому что я обнаружил, что он позволяет мне запрашивать только 100 строк данных за один раз. Таким образом, что мы можем сделать позже, так это использовать цикл for для изменения смещения и запроса большего количества точек данных.
Шаг 3. Извлеките полезную информацию, которая вам нужна
#read the data back as json file j=req.json() # Store data into the fields for item in j['products']: price.append(item['price']) location.append(item['originCode']) name.append(item['name']) ratingScore.append(item['leftView']['rateScore']) quantity.append(item['rightView']['text'].split(' Sold')[0] #Combine all the columns together output=pd.DataFrame({'Name':name,'price':price,'location':location,'Rating Score':ratingScore,'Quantity Sold':quantity})
Данные из API обычно довольно аккуратные и структурированные, поэтому я просто прочитал их в формате JSON. После этого я извлекаю полезные данные в разные столбцы и объединяю их вместе в качестве вывода. Вы можете увидеть вывод данных ниже.
Шаг 4. Для API с ограничением размера запроса вам нужно будет использовать цикл for для многократного получения всех данных
#Define API url url_search='https://sg-en-web-api.ezbuy.sg/api/EzCategory/ListProductsByCondition' #Define header for the post request headers={'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'} for i in range(0,14000,100): time.sleep(max(random.gauss(3,1),2)) print(i) data={ "searchCondition": {"categoryId":0,"freeShippingType":0,"filters": [],"keyWords":"mask"}, "limit":100, "offset":i, "language":"en", "dataType":"new" } req=s.post(url_search,headers=headers,json=data) j=req.json() for item in j['products']: price.append(item['price']) location.append(item['originCode']) name.append(item['name']) ratingScore.append(item['leftView']['rateScore']) quantity.append(item['rightView']['text'].split(' Sold')[0]) #Combine all the columns together output=pd.DataFrame({'Name':name,'price':price,'location':location,'Rating Score':ratingScore,'Quantity Sold':quantity})
Вот полный код для очистки всех строк данных маски лица в Ezbuy. Я обнаружил, что общее количество строк составляет 14 тыс., И поэтому я пишу цикл for, чтобы перебирать номер инкрементного смещения для запроса всех результатов. Еще одна важная вещь, на которую следует обратить внимание, это то, что я устанавливаю случайный тайм-аут в начале каждого цикла. Это потому, что я не хочу, чтобы очень частые HTTP-запросы вредили трафику веб-сайта и были замечены веб-сайтом.
Наконец, Рекомендация
Если вы хотите очистить веб-сайт, я бы предложил сначала проверить наличие API в разделе сети с помощью inspect. Если вы сможете найти ответ на запрос, который предоставит вам все необходимые данные, вы сможете создать стабильное и удобное решение. Если вы не можете найти данные в сети, вам следует попробовать использовать запросы или Selenium для загрузки содержимого HTML и использовать Beautiful Soup для форматирования данных. Наконец, используйте тайм-аут, чтобы избежать слишком частых посещений веб-сайта или API. Это может предотвратить блокировку вас сайтом и снизить посещаемость на благо сайта.