1. Code
  2. Python

Скрапинг веб-страниц в Python с Beautiful Soup: основы

В предыдущем уроке я показал вам, как использовать модуль Requests для доступа к веб-страницам с использованием Python. В этом учебном пособии рассмотрено множество тем, таких как получение запросов GET / POST и программная загрузка таких файлов, как изображения или PDF. Единственное, чего не было в этом учебнике - руководства по скрапингу веб-страниц, к которым вы обращались с помощью requests, для извлечения необходимой информации.
Scroll to top
This post is part of a series called Scraping Webpages in Python With Beautiful Soup.
Scraping Webpages in Python With Beautiful Soup: Search and DOM Modification

Russian (Pусский) translation by Anna k.Ivanova (you can also view the original English article)

В предыдущем уроке я показал вам, как использовать модуль Requests для доступа к веб-страницам с использованием Python. В этом учебном пособии рассмотрено множество тем, таких как получение запросов GET / POST и программная загрузка таких файлов, как изображения или PDF. Единственное, чего не было в этом учебнике - руководства по скрапингу веб-страниц, к которым вы обращались с помощью requests, для извлечения необходимой информации.

В этом уроке вы узнаете о Beautiful Soup, который представляет собой библиотеку Python для извлечения данных из файлов HTML. В этом уроке основное внимание будет уделено изучению основ библиотеки, а более подробные темы будут рассмотрены в следующем учебном пособии. Обратите внимание, что в этом руководстве для всех примеров используется Beautiful Soup 4.

Установка

Вы можете установить Beautiful Soup 4 с помощью pip. Название пакета beautifulsoup4. Он должен работать как на Python 2, так и на Python 3.

1
$ pip install beautifulsoup4

Если в вашей системе нет pip, вы можете напрямую загрузить исходный tarball Beautiful Soup 4 и установить его с помощью setup.py.

1
$ python setup.py install

BeautifulSoup изначально упакован как код Python 2. Когда вы устанавливаете его для использования с Python 3, он автоматически обновляется до кода Python 3. Код не будет конвертирован, если вы не установите пакет. Вот несколько распространенных ошибок, которые вы могли заметить:

  • «Нет модуля с именем HTMLParser» ImportError возникает, когда вы запускаете версию кода Python 2 под Python 3.
  • «Нет модуля с именем html.parser» ImportError возникает, когда вы запускаете версию кода Python 3 под Python 2.

Обе приведенные выше ошибки могут быть исправлены путем удаления и переустановки Beautiful Soup.

Установка парсера

Прежде чем обсуждать различия между различными парсерами, которые вы можете использовать с Beautiful Soup, давайте напишем код для создания soup.

1
from bs4 import BeautifulSoup
2
3
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "html.parser")

Объект BeautifulSoup может принимать два аргумента. Первым аргументом является фактическая разметка, а второй аргумент - синтаксический анализатор, который вы хотите использовать. Различные синтаксические анализаторы: html.parser, lxml и html5lib. Парсер lxml имеет две версии: парсер HTML и синтаксический анализатор XML.

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

1
$ pip install lxml
2
$ pip install html5lib

Парсер lxml работает очень быстро и может использоваться для быстрого анализа данных HTML. С другой стороны, парсер html5lib работает очень медленно, но он также очень мягкий. Ниже приведен пример использования каждого из этих синтаксических анализаторов:

1
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "html.parser")
2
print(soup)
3
# <html><p>This is <b>invalid HTML</b></p></html>

4
5
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "lxml")
6
print(soup)
7
# <html><body><p>This is <b>invalid HTML</b></p></body></html>

8
9
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "xml")
10
print(soup)
11
# <?xml version="1.0" encoding="utf-8"?>

12
# <html><p>This is <b>invalid HTML</b></p></html>

13
14
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "html5lib")
15
print(soup)
16
# <html><head></head><body><p>This is <b>invalid HTML</b></p></body></html>

Различия, описанные в приведенном выше примере, имеют значение только при анализе невалидного HTML. Тем не менее, большая часть HTML в Интернете невалидна, и знание этих различий поможет вам отладить некоторые ошибки синтаксического анализа и решить, какой парсер вы хотите использовать в проекте. Как правило, анализатор lxml является очень хорошим выбором.

Объекты в Beautiful Soup

Beautiful Soup анализирует данный HTML-документ в дерево объектов Python. Есть четыре основных объекта Python, о которых вам нужно знать: Tag, NavigableString, BeautifulSoup и Comment.

Объект Tag ссылается на фактический тег XML или HTML в документе. Вы можете получить доступ к имени тега, используя tag.name. Вы также можете установить имя тега на что-то еще. Изменение имени будет видно в разметке, созданной Beautiful Soup.

Вы можете получить доступ к различным атрибутам, таким как класс и идентификатор тега, используя tag['class'] и tag['id'] соответственно. Вы также можете получить доступ ко всему словарю атрибутов с помощью tag.attrs. Вы также можете добавлять, удалять или изменять атрибуты тега. Атрибуты элемента, такие как class, который может принимать несколько значений, сохраняются в виде списка.

Текст в теге хранится как NavigableString в Beautiful Soup. Он имеет несколько полезных методов, таких как replace_with("string"), чтобы заменить текст в теге. Вы также можете преобразовать строку NavigableString в строку unicode, используя unicode().

Beautiful Soup также позволяет получить доступ к комментариям на веб-странице. Эти комментарии хранятся как объект Comment, который также является в основном NavigableString.

Вы уже узнали о объекте BeautifulSoup в предыдущем разделе. Он используется для представления документа в целом. Поскольку он не является фактическим объектом, он не имеет никаких имен или атрибутов.

Получение Title, Headings и Links

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

1
import requests
2
from bs4 import BeautifulSoup
3
4
req = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)')
5
soup = BeautifulSoup(req.text, "lxml")

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

1
soup.title
2
# <title>Python (programming language) - Wikipedia</title>

3
4
soup.title.name
5
# 'title'

6
7
soup.title.string
8
# 'Python (programming language) - Wikipedia'

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

1
soup.h1
2
# <h1 class="firstHeading" id="firstHeading" lang="en">Python (programming language)</h1>

3
4
soup.h1.string
5
# 'Python (programming language)'

6
7
soup.h1['class']
8
# ['firstHeading']

9
10
soup.h1['id']
11
# 'firstHeading'

12
13
soup.h1.attrs
14
# {'class': ['firstHeading'], 'id': 'firstHeading', 'lang': 'en'}

15
16
soup.h1['class'] = 'firstHeading, mainHeading'
17
soup.h1.string.replace_with("Python - Programming Language")
18
del soup.h1['lang']
19
del soup.h1['id']
20
21
soup.h1
22
# <h1 class="firstHeading, mainHeading">Python - Programming Language</h1>

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

1
for sub_heading in soup.find_all('h2'):
2
    print(sub_heading.text)
3
    
4
# all the sub-headings like Contents, History[edit]...

Навигация по DOM

Вы можете перемещаться по дереву DOM с помощью регулярных имен тегов. Связывание имен тегов может помочь вам более глубоко перемещаться по дереву. Например, вы можете получить первую ссылку в первом абзаце данной страницы Википедии, используя soup.p.a. Все ссылки в первом абзаце можно получить, используя soup.p.find_all('a').

Вы также можете получить доступ ко всем дочерним элементам тега в виде списка с помощью tag.contents. Чтобы получить детей по определенному индексу, вы можете использовать tag.contents[index]. Вы также можете перебирать дочерние теги с помощью атрибута .children.

Оба .children и .contents полезны только тогда, когда вы хотите получить доступ к потомкам первого или первого уровня тега. Чтобы получить всех потомков, вы можете использовать атрибут .descendants.

1
print(soup.p.contents)
2
# [<b>Python</b>, ' is a widely used ',.....the full list]

3
4
print(soup.p.contents[10])
5
# <a href="/wiki/Readability" title="Readability">readability</a>

6
7
for child in soup.p.children:
8
    print(child.name)
9
# b

10
# None

11
# a

12
# None

13
# a

14
# None

15
# ... and so on.

Вы также можете получить доступ к родительскому элементу элемента, используя атрибут .parent. Аналогично, вы можете получить доступ ко всем предкам элемента, используя атрибут .parents. Родитель тега <html> верхнего уровня - это сам объект BeautifulSoup, а его родительский элемент - None.

1
print(soup.p.parent.name)
2
# div

3
4
for parent in soup.p.parents:
5
    print(parent.name)
6
# div

7
# div

8
# div

9
# body

10
# html

11
# [document]

Вы можете получить доступ к предыдущему и следующему родственнику элемента, используя атрибуты .previous_sibling и .next_sibling.

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

Вы также можете перебирать всех братьев элемента с использованием .previous_siblings и .next_siblings.

1
soup.head.next_sibling
2
# '\n'

3
4
soup.p.a.next_sibling
5
# ' for '

6
7
soup.p.a.previous_sibling
8
# ' is a widely used '

9
10
print(soup.p.b.previous_sibling)
11
# None

Вы можете перейти к элементу, который приходит сразу после текущего элемента, используя атрибут .next_element. Чтобы получить доступ к элементу, который приходит непосредственно перед текущим элементом, используйте атрибут .previous_element.

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

Финальные мысли

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

В следующей части этой серии вы узнаете, как использовать библиотеку Beautiful Soup для поиска и изменения DOM.