Реализация веб-парсинга в Python с помощью BeautifulSoup

Существует два основных способа извлечения данных с веб-сайта:

  • Использовать API сайта (если он существует). Например, у Facebook есть Facebook Graph API, который позволяет извлекать данные, размещенные на Facebook.
  • Получить доступ к HTML-странице сайта и извлечь из нее полезную информацию/данные. Эта техника называется веб-парсинг, веб-сборкой или извлечением данных из веб-страницы.

В этой статье рассматриваются шаги, связанные с веб-парсингом, на примере реализации фреймворка для веб-парсинга на языке Python под названием Beautiful Soup. Шаги, выполняемые при веб-парсинге:

  1. Отправьте HTTP-запрос на URL веб-страницы, к которой вы хотите получить доступ. Сервер отвечает на запрос, возвращая HTML-содержимое веб-страницы. Для решения этой задачи мы будем использовать стороннюю HTTP-библиотеку python-requests.
  2. После того как мы получили доступ к HTML-содержимому, нам остается разобрать данные. Поскольку большая часть HTML-данных является вложенной, мы не можем извлечь данные просто путем обработки строк. Необходим парсер, способный создать вложенную/деревянную структуру HTML-данных. Существует множество библиотек парсеров HTML, но наиболее продвинутой из них является html5lib.
  3. Теперь нам необходимо выполнить навигацию и поиск по созданному нами дереву разбора, т.е. обход дерева. Для этой задачи мы будем использовать еще одну стороннюю библиотеку Python - Beautiful Soup. Это библиотека Python для извлечения данных из HTML- и XML-файлов.

Шаг 1: Установка необходимых сторонних библиотек

Наиболее простым способом установки внешних библиотек в python является использование pip. pip - это система управления пакетами, которая используется для установки и управления пакетами программ, написанных на Python. Все, что вам нужно сделать, это:

pip install requests
pip install html5lib
pip install bs4

Другой способ - загрузить их вручную по этим ссылкам:

requests
html5lib
beautifulsoup4

Шаг 2: Получение доступа к HTML-содержимому веб-страницы 

импорт запросов

URL = "https://www.geeksforgeeks.org/data-structures/"
r = requests.get(URL)
print(r.content)

Попробуем разобраться в этом фрагменте кода.

  • Прежде всего, импортируйте библиотеку requests.
  • Затем укажите URL-адрес веб-страницы, с которой необходимо произвести сканирование.
  • Отправьте HTTP-запрос на указанный URL и сохраните ответ от сервера в объекте response с именем r.
  • Теперь в качестве print r.content получите необработанное HTML-содержимое веб-страницы. Оно имеет тип 'string'.

headers = {'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246"}
# Здесь указан агент пользователя для браузера Edge на windows 10. Пользовательский агент браузера можно узнать по приведенной выше ссылке.
r = requests.get(url=URL, headers=headers)
print(r.content)

Шаг 3: Разбор HTML-содержимого 

#Это не будет выполняться в онлайн IDE
import requests
from bs4 import BeautifulSoup
  
URL = "http://www.values.com/inspirational-quotes"
r = requests.get(URL)
  
soup = BeautifulSoup(r.content, 'html5lib') # If this line causes an error, run 'pip install html5lib' or install html5lib
print(soup.prettify())

Очень приятным моментом в библиотеке BeautifulSoup является то, что она построена поверх библиотек разбора HTML, таких как html5lib, lxml, html.parser и т.д. Таким образом, объект BeautifulSoup и указание библиотеки парсера могут быть созданы одновременно. В приведенном примере,

soup = BeautifulSoup(r.content, 'html5lib')

Мы создаем объект BeautifulSoup, передавая ему два аргумента:

  • r.content: Это исходный HTML-контент.
  • html5lib: указывается парсер HTML, который мы хотим использовать.

Теперь выводится soup.prettify(), который дает визуальное представление дерева разбора, созданного из исходного HTML-содержимого.

Шаг 4: Поиск и навигация по дереву разбора

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

#Python-программа для сканирования веб-сайта 
#и сохранения цитат с сайта
import requests
from bs4 import BeautifulSoup
import csv
   
URL = "http://www.values.com/inspirational-quotes"
r = requests.get(URL)
   
soup = BeautifulSoup(r.content, 'html5lib')
   
quotes=[]  # a list to store quotes
   
table = soup.find('div', attrs = {'id':'all_quotes'}) 
   
for row in table.findAll('div',
                         attrs = {'class':'col-6 col-lg-3 text-center margin-30px-bottom sm-margin-30px-top'}):
    quote = {}
    quote['theme'] = row.h5.text
    quote['url'] = row.a['href']
    quote['img'] = row.img['src']
    quote['lines'] = row.img['alt'].split(" #")[0]
    quote['author'] = row.img['alt'].split(" #")[1]
    quotes.append(quote)
   
filename = 'inspirational_quotes.csv'
with open(filename, 'w', newline='') as f:
    w = csv.DictWriter(f,['theme','url','img','lines','author'])
    w.writeheader()
    for quote in quotes:
        w.writerow(quote)

Замечено, что все кавычки находятся внутри контейнера div, id которого 'all_quotes'. Поэтому мы находим этот элемент div (в приведенном выше коде он обозначен как table) с помощью метода find():

table = soup.find('div', attrs = {'id':'all_quotes'}) 

Первым аргументом является HTML-тег, по которому производится поиск, а вторым - элемент словарного типа для указания дополнительных атрибутов, связанных с этим тегом. Метод find() возвращает первый подходящий элемент. Вы можете попробовать напечатать table.prettify(), <\/strong>чтобы получить представление о том, что делает эта часть кода.
Теперь в элементе таблицы можно заметить, что каждая цитата находится внутри контейнера div, класс которого - quote
. Поэтому мы перебираем все div-контейнеры, класс которых равен quote. Здесь мы используем метод findAll(), который по аргументам похож на метод find, но возвращает список всех совпадающих элементов. Теперь каждая цитата итерируется с помощью переменной row. Для лучшего понимания приведен пример HTML-содержимого строки: Implementing Web Scraping in Python with Beautiful Soup <\/strong>Теперь рассмотрим этот фрагмент кода:

for row in table.find_all_next('div', attrs = {'class': 'col-6 col-lg-3 text-center margin-30px-bottom sm-margin-30px-top'}):
    quote = {}
    quote['theme'] = row.h5.text
    quote['url'] = row.a['href']
    quote['img'] = row.img['src']
    quote['lines'] = row.img['alt'].split(" #")[0]
    quote['author'] = row.img['alt'].split(" #")[1]
    quotes.append(quote)

Мы создаем словарь для сохранения всей информации о цитате. Доступ к вложенной структуре можно получить с помощью точечной нотации. Для доступа к тексту внутри HTML-элемента мы используем .text : 

quote['theme'] = row.h5.text

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

quote['url'] = row.a['href'].

Наконец, все кавычки добавляются в список под названием quotes.
Наконец, мы хотим сохранить все наши данные в некотором CSV-файле.

filename = 'inspirational_quotes.csv'
with open(filename, 'w', newline='') as f:
    w = csv.DictWriter(f,['theme','url','img','lines','author'])
    w.writeheader()
    for quote in quotes:
        w.writerow(quote)

Здесь мы создаем CSV-файл inspirational_quotes.csv и сохраняем в нем все цитаты для дальнейшего использования.
Итак, это был простой пример создания веб-парсера на языке Python. Далее вы можете попробовать создать любой другой сайт по вашему выбору.