Статьи

Соскоб веб-страниц в Python с красивым супом: основы

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

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

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

1
$ pip install beautifulsoup4

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

1
$ python setup.py install

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

  • Ошибка ImportError «Нет модуля с именем HTMLParser» возникает при запуске версии кода Python 2 под Python 3.
  • Ошибка «Не ImportError модуль с именем html.parser» возникает при запуске версии кода Python 3 под Python 2.

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

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

1
2
3
from bs4 import BeautifulSoup
 
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
2
$ pip install lxml
$ pip install html5lib

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
soup = BeautifulSoup(«<html><p>This is <b>invalid HTML</p></html>», «html.parser»)
print(soup)
# <html><p>This is <b>invalid HTML</b></p></html>
 
soup = BeautifulSoup(«<html><p>This is <b>invalid HTML</p></html>», «lxml»)
print(soup)
# <html><body><p>This is <b>invalid HTML</b></p></body></html>
 
soup = BeautifulSoup(«<html><p>This is <b>invalid HTML</p></html>», «xml»)
print(soup)
# <?xml version=»1.0″ encoding=»utf-8″?>
# <html><p>This is <b>invalid HTML</b></p></html>
 
soup = BeautifulSoup(«<html><p>This is <b>invalid HTML</p></html>», «html5lib»)
print(soup)
# <html><head></head><body><p>This is <b>invalid HTML</b></p></body></html>

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

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() .

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

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

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

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

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

1
2
3
4
5
6
7
8
soup.title
# <title>Python (programming language) — Wikipedia</title>
 
soup.title.name
# ‘title’
 
soup.title.string
# ‘Python (programming language) — Wikipedia’

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
soup.h1
# <h1 class=»firstHeading» id=»firstHeading» lang=»en»>Python (programming language)</h1>
 
soup.h1.string
# ‘Python (programming language)’
 
soup.h1[‘class’]
# [‘firstHeading’]
 
soup.h1[‘id’]
# ‘firstHeading’
 
soup.h1.attrs
# {‘class’: [‘firstHeading’], ‘id’: ‘firstHeading’, ‘lang’: ‘en’}
 
soup.h1[‘class’] = ‘firstHeading, mainHeading’
soup.h1.string.replace_with(«Python — Programming Language»)
del soup.h1[‘lang’]
del soup.h1[‘id’]
 
soup.h1
# <h1 class=»firstHeading, mainHeading»>Python — Programming Language</h1>

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

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

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

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
print(soup.p.contents)
# [<b>Python</b>, ‘ is a widely used ‘,…..the full list]
 
print(soup.p.contents[10])
# <a href=»/wiki/Readability» title=»Readability»>readability</a>
 
for child in soup.p.children:
    print(child.name)
# b
# None
# a
# None
# a
# None
# … and so on.

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

01
02
03
04
05
06
07
08
09
10
11
print(soup.p.parent.name)
# div
 
for parent in soup.p.parents:
    print(parent.name)
# div
# div
# div
# body
# html
# [document]

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

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

Вы также можете перебрать все элементы одного элемента, используя .previous_siblings и .next_siblings .

01
02
03
04
05
06
07
08
09
10
11
soup.head.next_sibling
# ‘\n’
 
soup.panext_sibling
# ‘ for ‘
 
soup.paprevious_sibling
# ‘ is a widely used ‘
 
print(soup.pbprevious_sibling)
# None

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

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

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

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