Beautiful Soup - Навигация по тегам

В этой главе мы обсудим навигацию по тегам.

Ниже наш html-документ -

>>> html_doc = """
<html><head><title>Tutorials Point</title></head>
<body>
<p class="title"><b>The Biggest Online Tutorials Library, It's all Free</b></p>
<p class="prog">Top 5 most used Programming Languages are:
<a href="https://www.tutorialspoint.com/java/java_overview.htm" class="prog" id="link1">Java</a>,
<a href="https://www.tutorialspoint.com/cprogramming/index.htm" class="prog" id="link2">C</a>,
<a href="https://www.tutorialspoint.com/python/index.htm" class="prog" id="link3">Python</a>,
<a href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" class="prog" id="link4">JavaScript</a> and
<a href="https://www.tutorialspoint.com/ruby/index.htm" class="prog" id="link5">C</a>;
as per online survey.</p>
<p class="prog">Programming Languages</p>
"""
>>>
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(html_doc, 'html.parser')
>>>

Основываясь на приведенном выше документе, мы попытаемся перейти от одной части документа к другой.

Спускаться

Одной из важных частей любого элемента HTML-документа являются теги, которые могут содержать другие теги / строки (дочерние элементы тегов). Beautiful Soup предоставляет различные способы навигации и перебора дочерних элементов тега.

Навигация с использованием имен тегов

Самый простой способ поиска в дереве синтаксического анализа - поиск тега по его имени. Если вам нужен тег <head>, используйте soup.head -

>>> soup.head
<head>&t;title>Tutorials Point</title></head>
>>> soup.title
<title>Tutorials Point</title>

Чтобы получить определенный тег (например, первый тег <b>) в теге <body>.

>>> soup.body.b
<b>The Biggest Online Tutorials Library, It's all Free</b>

Использование имени тега в качестве атрибута даст вам только первый тег с этим именем -

>>> soup.a
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>

Чтобы получить все атрибуты тега, вы можете использовать метод find_all () -

>>> soup.find_all("a")
[<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>, <a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>, <a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>, <a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>, <a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>]>>> soup.find_all("a")
[<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>, <a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>, <a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>, <a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>, <a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>]

.contents и .children

Мы можем искать дочерние элементы тега в списке по его .contents -

>>> head_tag = soup.head
>>> head_tag
<head><title>Tutorials Point</title></head>
>>> Htag = soup.head
>>> Htag
<head><title>Tutorials Point</title></head>
>>>
>>> Htag.contents
[<title>Tutorials Point</title>
>>>
>>> Ttag = head_tag.contents[0]
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.contents
['Tutorials Point']

У самого объекта BeautifulSoup есть дочерние элементы. В этом случае тег <html> является потомком объекта BeautifulSoup -

>>> len(soup.contents)
2
>>> soup.contents[1].name
'html'

Строка не имеет .contents, потому что она ничего не может содержать -

>>> text = Ttag.contents[0]
>>> text.contents
self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'contents'

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

>>> for child in Ttag.children:
print(child)
Tutorials Point

потомки

Атрибут .descendants позволяет вам рекурсивно перебирать всех дочерних элементов тега -

его прямые дети и дети его прямых детей и так далее -

>>> for child in Htag.descendants:
print(child)
<title>Tutorials Point</title>
Tutorials Point

Тег <head> имеет только один дочерний элемент, но имеет два потомка: тег <title> и дочерний элемент тега <title>. У объекта beautifulsoup есть только один прямой дочерний элемент (тег <html>), но у него много потомков -

>>> len(list(soup.children))
2
>>> len(list(soup.descendants))
33

.string

Если у тега только один дочерний элемент, и этот дочерний элемент является NavigableString, дочерний элемент становится доступным как .string -

>>> Ttag.string
'Tutorials Point'

Если единственным дочерним элементом тега является другой тег, и этот тег имеет .string, то считается, что родительский тег имеет ту же .string, что и его дочерний тег -

>>> Htag.contents
[<title>Tutorials Point</title>]
>>>
>>> Htag.string
'Tutorials Point'

Однако, если тег содержит более одного элемента, тогда неясно, на что следует ссылаться .string, поэтому .string определяется как None -

>>> print(soup.html.string)
None

.strings и stripped_strings

Если внутри тега более одного объекта, вы все равно можете просматривать только строки. Используйте генератор .strings -

>>> for string in soup.strings:
print(repr(string))
'\n'
'Tutorials Point'
'\n'
'\n'
"The Biggest Online Tutorials Library, It's all Free"
'\n'
'Top 5 most used Programming Languages are: \n'
'Java'
',\n'
'C'
',\n'
'Python'
',\n'
'JavaScript'
' and\n'
'C'
';\n \nas per online survey.'
'\n'
'Programming Languages'
'\n'

Чтобы удалить лишние пробелы, используйте генератор .stripped_strings -

>>> for string in soup.stripped_strings:
print(repr(string))
'Tutorials Point'
"The Biggest Online Tutorials Library, It's all Free"
'Top 5 most used Programming Languages are:'
'Java'
','
'C'
','
'Python'
','
'JavaScript'
'and'
'C'
';\n \nas per online survey.'
'Programming Languages'

Подниматься

В аналогии с «семейным деревом» каждый тег и каждая строка имеет родителя: тег, который его содержит:

.parent

Чтобы получить доступ к родительскому элементу элемента, используйте атрибут .parent.

>>> Ttag = soup.title
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.parent
<head>title>Tutorials Point</title></head>

В нашем html_doc сама строка заголовка имеет родителя: тег <title>, который ее содержит -

>>> Ttag.string.parent
<title>Tutorials Point</title>

Родителем тега верхнего уровня, такого как <html>, является сам объект Beautifulsoup -

>>> htmltag = soup.html
>>> type(htmltag.parent)
<class 'bs4.BeautifulSoup'>

Родитель объекта Beautifulsoup определяется как None -

>>> print(soup.parent)
None

.родители

Чтобы перебрать все родительские элементы, используйте атрибут .parents.

>>> link = soup.a
>>> link
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
>>>
>>> for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
p
body
html
[document]

Идти боком

Ниже один простой документ -

>>> sibling_soup = BeautifulSoup("<a><b>TutorialsPoint</b><c><strong>The Biggest Online Tutorials Library, It's all Free</strong></b></a>")
>>> print(sibling_soup.prettify())
<html>
<body>
   <a>
      <b>
         TutorialsPoint
      </b>
      <c>
         <strong>
            The Biggest Online Tutorials Library, It's all Free
         </strong>
      </c>
   </a>
</body>
</html>

В приведенном выше документе теги <b> и <c> находятся на одном уровне и являются дочерними элементами одного и того же тега. Оба тега <b> и <c> являются братьями и сестрами.

.next_sibling и .previous_sibling

Используйте .next_sibling и .previous_sibling для перехода между элементами страницы, которые находятся на одном уровне дерева синтаксического анализа:

>>> sibling_soup.b.next_sibling
<c><strong>The Biggest Online Tutorials Library, It's all Free</strong></c>
>>>
>>> sibling_soup.c.previous_sibling
<b>TutorialsPoint</b>

Тег <b> имеет .next_sibling, но не имеет .previous_sibling, поскольку на том же уровне дерева перед тегом <b> нет ничего, тот же случай с тегом <c>.

>>> print(sibling_soup.b.previous_sibling)
None
>>> print(sibling_soup.c.next_sibling)
None

Две строки не являются братьями и сестрами, поскольку у них нет одного и того же родителя.

>>> sibling_soup.b.string
'TutorialsPoint'
>>>
>>> print(sibling_soup.b.string.next_sibling)
None

.next_siblings и .previous_siblings

Чтобы перебирать родственников тега, используйте .next_siblings и .previous_siblings.

>>> for sibling in soup.a.next_siblings:
print(repr(sibling))
',\n'
<a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>
',\n'
>a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>
',\n'
<a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>
' and\n'
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm"
id="link5">C</a>
';\n \nas per online survey.'
>>> for sibling in soup.find(id="link3").previous_siblings:
print(repr(sibling))
',\n'
<a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>
',\n'
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
'Top 5 most used Programming Languages are: \n'

Туда и обратно

Теперь давайте вернемся к первым двум строкам в нашем предыдущем примере «html_doc» -

&t;html><head><title>Tutorials Point</title></head>
<body>
<h4 class="tagLine"><b>The Biggest Online Tutorials Library, It's all Free</b></h4>

Анализатор HTML берет указанную выше строку символов и превращает ее в серию событий, таких как «открыть тег <html>», «открыть тег <head>», «открыть тег <title>», «добавить строку», «Закрыть тег </title>», «закрыть тег </head>», «открыть тег <h4>» и так далее. BeautifulSoup предлагает различные методы для восстановления исходного синтаксического анализа документа.

.next_element и .previous_element

Атрибут .next_element тега или строки указывает на то, что было проанализировано сразу после этого. Иногда это похоже на .next_sibling, но не совсем то же самое. Ниже показан последний тег <a> в нашем примере документа «html_doc».

>>> last_a_tag = soup.find("a", id="link5")
>>> last_a_tag
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>
>>> last_a_tag.next_sibling
';\n \nas per online survey.'

Однако .next_element этого тега <a>, то, что было проанализировано сразу после тега <a>, не является остальной частью этого предложения: это слово «C»:

>>> last_a_tag.next_element
'C'

Вышеуказанное поведение объясняется тем, что в исходной разметке перед точкой с запятой стояла буква «C». Парсер обнаружил тег <a>, затем букву «C», затем закрывающий тег </a>, затем точку с запятой и оставшуюся часть предложения. Точка с запятой находится на том же уровне, что и тег <a>, но буква «C» встречается первой.

Атрибут .previous_element - полная противоположность .next_element. Он указывает на любой элемент, который был проанализирован непосредственно перед этим.

>>> last_a_tag.previous_element
' and\n'
>>>
>>> last_a_tag.previous_element.next_element
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>

.next_elements и .previous_elements

Мы используем эти итераторы для перемещения вперед и назад к элементу.

>>> for element in last_a_tag.next_e lements:
print(repr(element))
'C'
';\n \nas per online survey.'
'\n'
<p class="prog">Programming Languages</p>
'Programming Languages'
'\n'