Как очистить данные с веб-сайта на Python

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

Что такое веб-парсинг

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

Можете ли вы очистить со всех веб-сайтов?

Парсинг вызывает всплеск трафика на веб-сайте и может вызвать сбой сервера веб-сайта. Таким образом, не все веб-сайты позволяют пользователям парсить. Как узнать, какие сайты разрешены, а какие нет? Вы можете посмотреть файл robots.txt на веб-сайте. Вы просто помещаете robots.txt после URL, который вы хотите очистить, и вы увидите информацию о том, позволяет ли хост веб-сайта очистить веб-сайт.

Возьмем для примера Google.com

Вы можете видеть, что Google не разрешает парсинг для многих своих дочерних веб-сайтов. Однако он допускает определенные пути, такие как «/ m / finance», и поэтому, если вы хотите собирать информацию о финансах, это полностью законное место для очистки.

Еще одно замечание: это видно из первой строки User-agent. Здесь Google определяет правила для всех пользовательских агентов, но веб-сайт может предоставить определенным пользовательским агентам специальные разрешения, поэтому вы можете захотеть обратиться к информации там.

Как работает парсинг?

Веб-парсинг работает так же, как бот, просматривающий разные страницы веб-сайта и копирующий все содержимое. Когда вы запускаете код, он отправляет запрос на сервер, и данные содержатся в полученном вами ответе. Затем вы анализируете данные ответа и извлекаете нужные части.

Как мы выполняем парсинг веб-страниц?

Хорошо, наконец-то мы здесь. Существует 2 разных подхода к парсингу веб-страниц в зависимости от того, как веб-сайт структурирует свое содержимое.

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

Примерно 5 шагов, как показано ниже:

  1. Проверьте HTML-код веб-сайта, который вы хотите просканировать.
  2. Получите доступ к URL-адресу веб-сайта с помощью кода и загрузите все содержимое HTML на странице
  3. Отформатируйте загруженный контент в удобочитаемый формат
  4. Извлеките полезную информацию и сохраните ее в структурированном формате
  5. Для информации, отображаемой на нескольких страницах веб-сайта, вам может потребоваться повторить шаги 2–4, чтобы получить полную информацию.

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

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

Шаги:

  1. Проверьте сетевой раздел XHR URL-адреса, который вы хотите сканировать.
  2. Найдите запрос-ответ, который дает вам нужные данные
  3. В зависимости от типа запроса (отправка или получение), а также заголовка и полезной нагрузки запроса, смоделируйте запрос в своем коде и получите данные из API. Обычно данные, полученные от API, имеют довольно аккуратный формат.
  4. Извлеките полезную информацию, которая вам нужна
  5. Для 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. Это может предотвратить блокировку вас сайтом и снизить посещаемость на благо сайта.