Разбор HTML с помощью BeautifulSoup в Python

Введение Веб-скрапинг - это программный сбор информации с различных веб-сайтов. Хотя существует множество библиотек и фреймворков на разных языках, которые могут извлекать веб-данные, Python долгое время был популярным выбором из-за множества опций для парсинга веб-страниц. Эта статья даст вам ускоренный курс по парсингу веб-страниц в Python с помощью Beautiful Soup - популярной библиотеки Python для синтаксического анализа HTML и XML. Этичный парсинг веб-скрапинга Веб-парсинг является повсеместным и дает нам данные, как если бы мы

Вступление

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

Эта статья даст вам ускоренный курс по парсингу веб-страниц в Python с помощью Beautiful Soup - популярной библиотеки Python для синтаксического анализа HTML и XML.

Этичный парсинг веб-сайтов

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

  • Не заявляйте, что извлеченный контент принадлежит нам. Владельцы веб-сайтов иногда тратят много времени на создание статей, сбор сведений о продуктах или сбор другого контента. Мы должны уважать их труд и оригинальность.
  • Не очищайте веб-сайт, который не нужно очищать. Веб-сайты иногда поставляются с robots.txt который определяет части веб-сайта, которые можно очистить. У многих веб-сайтов также есть Условия использования, которые могут не разрешать очистку. Мы должны уважать веб-сайты, которые не хотят обрабатывать.
  • Есть ли уже доступный API? Прекрасно, нам не нужно писать скребок. API-интерфейсы создаются для предоставления доступа к данным контролируемым способом, определенным владельцами данных. Мы предпочитаем использовать API, если они доступны.
  • Выполнение запросов к веб-сайту может отрицательно сказаться на его работе. Веб-парсер, который делает слишком много запросов, может быть таким же изнурительным, как и DDOS-атака. Мы должны выполнять очистку со всей ответственностью, чтобы не нарушить нормальную работу веб-сайта.

Обзор Beautiful Soup

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

Что делает Beautiful Soup таким полезным, так это множество функций, которые он предоставляет для извлечения данных из HTML. На этом изображении ниже показаны некоторые функции, которые мы можем использовать:

BeautifulSoup -Обзор{.ezlazyload}

Давайте на практике посмотрим, как мы можем анализировать HTML с помощью Beautiful Soup. Рассмотрим следующую HTML-страницу, сохраненную в файле как doc.html :

 <html> 
 <head> 
 <title>Head's title</title> 
 </head> 
 
 <body> 
 <p class="title"><b>Body's title</b></p> 
 <p class="story">line begins 
 <a href="http://example.com/element1" class="element" id="link1">1</a> 
 <a href="http://example.com/element2" class="element" id="link2">2</a> 
 <a href="http://example.com/avatar1" class="avatar" id="link3">3</a> 
 <p> line ends</p> 
 </body> 
 </html> 

Следующие ниже фрагменты кода протестированы на Ubuntu 20.04.1 LTS . Вы можете установить BeautifulSoup , набрав в терминале следующую команду:

 $ pip3 install beautifulsoup4 

Необходимо подготовить HTML-файл doc.html . Это делается путем передачи файла BeautifulSoup , давайте воспользуемся для этого интерактивной оболочкой Python, чтобы мы могли мгновенно распечатать содержимое определенной части страницы:

 from bs4 import BeautifulSoup 
 
 with open("doc.html") as fp: 
 soup = BeautifulSoup(fp, "html.parser") 

Теперь мы можем использовать Beautiful Soup для навигации по нашему веб-сайту и извлечения данных.

Из объекта супа, созданного в предыдущем разделе, возьмем тег заголовка doc.html :

 soup.head.title # returns <title>Head's title</title> 

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

Навигация по определеннымтегам{.ezlazyload}

Beautiful Soup - мощный инструмент, потому что наши объекты Python соответствуют вложенной структуре HTML-документа, который мы очищаем.

Чтобы получить текст первого <a> , введите следующее:

 soup.body.a.text # returns '1' 

Чтобы получить заголовок в теге body HTML (обозначается классом title), введите в терминале следующее:

 soup.body.pb # returns <b>Body's title</b> 

Для глубоко вложенных HTML-документов навигация может быстро стать утомительной. К счастью, Beautiful Soup поставляется с функцией поиска, поэтому нам не нужно перемещаться, чтобы получить элементы HTML.

Поиск элементов тегов

Метод find_all() принимает HTML-тег в качестве строкового аргумента и возвращает список элементов, соответствующих указанному тегу. Например, если мы хотим , чтобы все теги в a doc.html :

 soup.find_all("a") 

Мы видим этот список a тег , как вывод:

 [<a class="element" href="http://example.com/element1" id="link1">1</a>, <a class="element" href="http://example.com/element2" id="link2">2</a>, <a class="element" href="http://example.com/element3" id="link3">3</a>] 

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

Поиск элементовтегов{.ezlazyload}

Мы также можем искать теги определенного класса, class_ аргумент class_. Beautiful Soup использует class_ потому что class - это зарезервированное ключевое слово в Python. Давайте искать все a теги , которые имеют класс «элемент»:

 soup.find_all("a", class_="element") 

Поскольку у нас есть только две ссылки с классом "element", вы увидите следующий результат:

 [<a class="element" href="http://example.com/element1" id="link1">1</a>, <a class="element" href="http://example.com/element2" id="link2">2</a>] 

Что, если бы мы хотели получить ссылки, встроенные в теги a Давайте href ссылки, используя опцию find() Он работает так же, как find_all() но возвращает первый соответствующий элемент вместо списка. Введите это в свою оболочку:

 soup.find("a", href=True)["href"] # returns http://example.com/element1 

Функции find() и find_all() также принимают регулярное выражение вместо строки. За кулисами текст будет отфильтрован с использованием метода search() Например:

 import re 
 
 for tag in soup.find_all(re.compile("^b")): 
 print(tag) 

Список после итерации выбирает теги, начинающиеся с символа b который включает <body> и <b> :

 <body> 
 <p class="title"><b>Body's title</b></p> 
 <p class="story">line begins 
 <a class="element" href="http://example.com/element1" id="link1">1</a> 
 <a class="element" href="http://example.com/element2" id="link2">2</a> 
 <a class="element" href="http://example.com/element3" id="link3">3</a> 
 <p> line ends</p> 
 </p></body> 
 <b>Body's title</b> 

Мы рассмотрели наиболее популярные способы получения тегов и их атрибутов. Иногда, особенно для менее динамичных веб-страниц, нам просто нужен текст. Посмотрим, как мы сможем это получить!

Получение всего текста

Функция get_text() извлекает весь текст из HTML-документа. Получим весь текст HTML-документа:

 soup.get_text() 

Ваш результат должен быть таким:

 Head's title 
 
 
 Body's title 
 line begins 
 1 
 2 
 3 
 line ends 

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

 "\n\nHead's title\n\n\nBody's title\nline begins\n 1\n2\n3\n line ends\n\n" 

Теперь, когда мы понимаем, как использовать Beautiful Soup, давайте очистим веб-сайт!

Красивый суп в действии - очистка списка книг

Теперь, когда мы освоили компоненты Beautiful Soup, пришло время применить наши знания на практике. Давайте создадим парсер для извлечения данных из https://books.toscrape.com/ и сохранения их в файл CSV. Сайт содержит случайные данные о книгах и является отличным местом для тестирования ваших методов парсинга.

Сначала создайте новый файл с именем scraper.py . Импортируем все библиотеки, которые нам нужны для этого скрипта:

 import requests 
 import time 
 import csv 
 import re 
 from bs4 import BeautifulSoup 

В упомянутых выше модулях:

  • requests - выполняет запрос URL и извлекает HTML-код веб-сайта.
  • time - ограничивает, сколько раз мы очищаем страницу за один раз
  • csv - помогает нам экспортировать очищенные данные в файл CSV.
  • re - позволяет нам писать регулярные выражения, которые пригодятся для выбора текста на основе его шаблона
  • bs4 - с уважением, модуль парсинга HTML

У вас bs4 , а time , csv и re - встроенные пакеты в Python. Вам нужно будет установить requests напрямую следующим образом:

 $ pip3 install requests 

Прежде чем начать, вам необходимо понять, как структурирован HTML-код веб-страницы. В вашем браузере перейдите по адресу http://books.toscrape.com/catalogue/page-1.html . Затем щелкните правой кнопкой мыши компоненты веб-страницы, которые нужно очистить, и нажмите кнопку проверки , чтобы понять иерархию тегов, как показано ниже.

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

ПониманиеHTML-тегов{.ezlazyload}

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

 def scrape(source_url, soup): # Takes the driver and the subdomain for concats as params 
 # Find the elements of the article tag 
 books = soup.find_all("article", class_="product_pod") 
 
 # Iterate over each book article tag 
 for each_book in books: 
 info_url = source_url+"/"+each_book.h3.find("a")["href"] 
 cover_url = source_url+"/catalogue" + \ 
 each_book.a.img["src"].replace("..", "") 
 
 title = each_book.h3.find("a")["title"] 
 rating = each_book.find("p", class_="star-rating")["class"][1] 
 # can also be written as : each_book.h3.find("a").get("title") 
 price = each_book.find("p", class_="price_color").text.strip().encode( 
 "ascii", "ignore").decode("ascii") 
 availability = each_book.find( 
 "p", class_="instock availability").text.strip() 
 
 # Invoke the write_to_csv function 
 write_to_csv([info_url, cover_url, title, rating, price, availability]) 

Последняя строка приведенного выше фрагмента указывает на функцию для записи списка очищенных строк в файл CSV. Давайте добавим эту функцию сейчас:

 def write_to_csv(list_input): 
 # The scraped info will be written to a CSV here. 
 try: 
 with open("allBooks.csv", "a") as fopen: # Open the csv file. 
 csv_writer = csv.writer(fopen) 
 csv_writer.writerow(list_input) 
 except: 
 return False 

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

Для этого давайте посмотрим на URL, для которого мы пишем этот парсер:

 "http://books.toscrape.com/catalogue/page-1.html" 

Единственным изменяющимся элементом URL-адреса является номер страницы. Мы можем отформатировать URL-адрес динамически, чтобы он стал исходным URL-адресом :

 "http://books.toscrape.com/catalogue/page-{}.html".format(str(page_number)) 

Этот строковый URL-адрес с номером страницы можно получить с помощью метода requests.get() . Затем мы можем создать новый объект BeautifulSoup Каждый раз, когда мы получаем объект супа, проверяется наличие кнопки «далее», чтобы мы могли остановиться на последней странице. Мы отслеживаем счетчик номера страницы, который увеличивается на 1 после успешного очистки страницы.

 def browse_and_scrape(seed_url, page_number=1): 
 # Fetch the URL - We will be using this to append to images and info routes 
 url_pat = re.compile(r"(http://.*\.com)") 
 source_url = url_pat.search(seed_url).group(0) 
 
 # Page_number from the argument gets formatted in the URL & Fetched 
 formatted_url = seed_url.format(str(page_number)) 
 
 try: 
 html_text = requests.get(formatted_url).text 
 # Prepare the soup 
 soup = BeautifulSoup(html_text, "html.parser") 
 print(f"Now Scraping - {formatted_url}") 
 
 # This if clause stops the script when it hits an empty page 
 if soup.find("li", class_="next") != None: 
 scrape(source_url, soup) # Invoke the scrape function 
 # Be a responsible citizen by waiting before you hit again 
 time.sleep(3) 
 page_number += 1 
 # Recursively invoke the same function with the increment 
 browse_and_scrape(seed_url, page_number) 
 else: 
 scrape(source_url, soup) # The script exits here 
 return True 
 return True 
 except Exception as e: 
 return e 

Вышеупомянутая функция, browse_and_scrape() , вызывается рекурсивно до тех пор, пока функция soup.find("li",class_="next") вернет None . На этом этапе код очистит оставшуюся часть веб-страницы и завершит работу.

Для последней части пазла мы запускаем процесс соскабливания. Мы определяем seed_url и вызываем browse_and_scrape() для получения данных. Это делается в блоке if __name__ == "__main__"

 if __name__ == "__main__": 
 seed_url = "http://books.toscrape.com/catalogue/page-{}.html" 
 print("Web scraping has begun") 
 result = browse_and_scrape(seed_url) 
 if result == True: 
 print("Web scraping is now complete!") 
 else: 
 print(f"Oops, That doesn't seem right!!! - {result}") 

Если вы хотите узнать больше о if __name__ == "__main__" , ознакомьтесь с нашим руководством о том, как он работает .

Вы можете выполнить сценарий, как показано ниже, в вашем терминале и получить вывод как:

 $ python scraper.py 

 Web scraping has begun 
 Now Scraping - http://books.toscrape.com/catalogue/page-1.html 
 Now Scraping - http://books.toscrape.com/catalogue/page-2.html 
 Now Scraping - http://books.toscrape.com/catalogue/page-3.html 
 . 
 . 
 . 
 Now Scraping - http://books.toscrape.com/catalogue/page-49.html 
 Now Scraping - http://books.toscrape.com/catalogue/page-50.html 
 Web scraping is now complete! 

Полученные данные можно найти в текущем рабочем каталоге под именем allBooks.csv . Вот пример содержимого файла:

 http://books.toscrape.com/a-light-in-the-attic_1000/index.html,http://books.toscrape.com/catalogue/media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg,A Light in the Attic,Three,51.77,In stock 
 http://books.toscrape.com/tipping-the-velvet_999/index.html,http://books.toscrape.com/catalogue/media/cache/26/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg,Tipping the Velvet,One,53.74,In stock 
 http://books.toscrape.com/soumission_998/index.html,http://books.toscrape.com/catalogue/media/cache/3e/ef/3eef99c9d9adef34639f510662022830.jpg,Soumission,One,50.10,In stock 

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

Заключение

В этом уроке мы узнали об этике написания хороших парсеров. Затем мы использовали Beautiful Soup для извлечения данных из HTML-файла с использованием свойств объекта Beautiful Soup и его различных методов, таких как find() , find_all() и get_text() . Затем мы создали парсер, который извлекает список книг в Интернете и экспортирует его в CSV.

Веб-скрапинг - полезный навык, который помогает в различных действиях, таких как извлечение данных, таких как API, выполнение контроля качества на веб-сайте, проверка неработающих URL-адресов на веб-сайте и многое другое. Какой следующий скребок вы собираетесь построить?

comments powered by Disqus