Beautiful Soup - Navigation par tags

Dans ce chapitre, nous discuterons de la navigation par balises.

Ci-dessous notre document 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')
>>>

Sur la base du document ci-dessus, nous essaierons de passer d'une partie du document à une autre.

Descente

Les balises, qui peuvent contenir d'autres balises / chaînes (enfants de balises), sont l'un des éléments importants de tout élément de document HTML. Beautiful Soup propose différentes façons de naviguer et de parcourir les enfants de l'étiquette.

Navigation à l'aide des noms de balises

Le moyen le plus simple de rechercher une arborescence d'analyse est de rechercher la balise par son nom. Si vous voulez la balise <head>, utilisez soup.head -

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

Pour obtenir une balise spécifique (comme la première balise <b>) dans la balise <body>.

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

L'utilisation d'un nom de balise comme attribut vous donnera uniquement la première balise de ce nom -

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

Pour obtenir tous les attributs de la balise, vous pouvez utiliser la méthode 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>]

.contenu et .enfants

Nous pouvons rechercher les enfants d'une balise dans une liste par son .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']

L'objet BeautifulSoup lui-même a des enfants. Dans ce cas, la balise <html> est l'enfant de l'objet BeautifulSoup -

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

Une chaîne n'a pas de .contents, car elle ne peut rien contenir -

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

Au lieu de les obtenir sous forme de liste, utilisez le générateur .children pour accéder aux enfants de la balise -

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

.descendance

L'attribut .descendants vous permet d'itérer sur tous les enfants d'une balise, de manière récursive -

ses enfants directs et les enfants de ses enfants directs et ainsi de suite -

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

La balise <head> n'a qu'un seul enfant, mais elle a deux descendants: la balise <title> et l'enfant de la balise <title>. L'objet beautifulsoup n'a qu'un seul enfant direct (la balise <html>), mais il a beaucoup de descendants -

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

.chaîne

Si la balise n'a qu'un seul enfant et que cet enfant est une chaîne NavigableString, l'enfant est rendu disponible en tant que .string -

>>> Ttag.string
'Tutorials Point'

Si le seul enfant d'une balise est une autre balise, et que cette balise a une .string, alors la balise parent est considérée comme ayant la même .string que son enfant -

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

Cependant, si une balise contient plus d'une chose, alors il n'est pas clair à quoi .string doit faire référence, donc .string est défini sur None -

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

.strings et stripped_strings

S'il y a plus d'une chose dans une balise, vous pouvez toujours regarder uniquement les chaînes. Utilisez le générateur .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'

Pour supprimer les espaces supplémentaires, utilisez le générateur .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'

Monter

Dans une analogie avec «arbre généalogique», chaque tag et chaque chaîne a un parent: le tag qui le contient:

.parent

Pour accéder à l'élément parent de l'élément, utilisez l'attribut .parent.

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

Dans notre html_doc, la chaîne de titre elle-même a un parent: la balise <title> qui la contient -

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

Le parent d'une balise de niveau supérieur comme <html> est l'objet Beautifulsoup lui-même -

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

Le .parent d'un objet Beautifulsoup est défini comme Aucun -

>>> print(soup.parent)
None

.Parents

Pour parcourir tous les éléments parents, utilisez l'attribut .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]

Aller de côté

Voici un simple 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>

Dans le document ci-dessus, les balises <b> et <c> sont au même niveau et elles sont toutes deux enfants de la même balise. Les balises <b> et <c> sont des frères et sœurs.

.next_sibling et .previous_sibling

Utilisez .next_sibling et .previous_sibling pour naviguer entre les éléments de page qui se trouvent au même niveau de l'arborescence d'analyse:

>>> 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>

La balise <b> a un .next_sibling mais pas de .previous_sibling, car il n'y a rien avant la balise <b> au même niveau de l'arborescence, même cas avec la balise <c>.

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

Les deux chaînes ne sont pas des frères et sœurs, car ils n'ont pas le même parent.

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

.next_siblings et .previous_siblings

Pour parcourir les frères et sœurs d'une balise, utilisez .next_siblings et .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'

Aller et venir

Revenons maintenant aux deux premières lignes de notre précédent exemple «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>

Un analyseur HTML prend ci-dessus la chaîne de caractères et la transforme en une série d'événements tels que «ouvrir une balise <html>», «ouvrir une balise <head>», «ouvrir la balise <title>», «ajouter une chaîne», «Fermez la balise </title>», «fermez la balise </head>», «ouvrez une balise <h4>» et ainsi de suite. BeautifulSoup propose différentes méthodes pour reconstruire l'analyse initiale du document.

.next_element et .previous_element

L'attribut .next_element d'une balise ou d'une chaîne pointe vers ce qui a été analysé immédiatement après. Parfois, il ressemble à .next_sibling, mais ce n'est pas entièrement le même. Vous trouverez ci-dessous la dernière balise <a> de notre exemple de document «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.'

Cependant, l'élément .next_element de cette balise <a>, la chose qui a été analysée immédiatement après la balise <a>, n'est pas le reste de cette phrase: c'est le mot «C»:

>>> last_a_tag.next_element
'C'

Le comportement ci-dessus est dû au fait que dans le balisage d'origine, la lettre «C» est apparue avant ce point-virgule. L'analyseur a rencontré une balise <a>, puis la lettre «C», puis la balise de fermeture </a>, puis le point-virgule et le reste de la phrase. Le point-virgule est au même niveau que la balise <a>, mais la lettre «C» a été rencontrée en premier.

L'attribut .previous_element est l'exact opposé de .next_element. Il pointe vers n'importe quel élément analysé immédiatement avant celui-ci.

>>> 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 et .previous_elements

Nous utilisons ces itérateurs pour avancer et reculer vers un élément.

>>> 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'