Скрапинг веб-страниц в Python с Beautiful Soup: основы
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.



