Beautiful Soup - Navigazione per tag

In questo capitolo discuteremo della navigazione per tag.

Di seguito è riportato il nostro documento 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')
>>>

Sulla base del documento sopra, proveremo a spostarci da una parte all'altra del documento.

Scendendo

Uno degli elementi più importanti in qualsiasi parte del documento HTML sono i tag, che possono contenere altri tag / stringhe (i figli dei tag). Beautiful Soup offre diversi modi per navigare e iterare sui figli dei tag.

Navigazione utilizzando i nomi dei tag

Il modo più semplice per cercare un albero di analisi è cercare il tag in base al nome. Se vuoi il tag <head>, usa soup.head -

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

Per ottenere un tag specifico (come il primo tag <b>) nel tag <body>.

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

L'utilizzo di un nome di tag come attributo ti darà solo il primo tag con quel nome -

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

Per ottenere tutti gli attributi del tag, puoi utilizzare il metodo 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>]

.contenuti e .bambini

Possiamo cercare i figli del tag in un elenco in base al suo contenuto.

>>> 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'oggetto BeautifulSoup stesso ha figli. In questo caso, il tag <html> è il figlio dell'oggetto BeautifulSoup -

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

Una stringa non ha .contents, perché non può contenere nulla -

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

Invece di ottenerli come un elenco, usa il generatore .children per accedere ai figli del tag -

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

.descendants

L'attributo .descendants ti consente di iterare su tutti i figli di un tag, in modo ricorsivo -

i suoi figli diretti e i figli dei suoi figli diretti e così via -

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

Il tag <head> ha un solo figlio, ma ha due discendenti: il tag <title> e il tag <title> figlio. L'oggetto beautifulsoup ha un solo figlio diretto (il tag <html>), ma ha molti discendenti -

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

.corda

Se il tag ha un solo elemento secondario e tale elemento secondario è una NavigableString, l'elemento secondario viene reso disponibile come .string -

>>> Ttag.string
'Tutorials Point'

Se l'unico tag secondario di un tag è un altro tag e quel tag ha una .string, allora il tag genitore è considerato avere la stessa .string del suo figlio -

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

Tuttavia, se un tag contiene più di una cosa, non è chiaro a cosa dovrebbe riferirsi .string, quindi .string è definito su Nessuno -

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

.strings e stripped_strings

Se c'è più di una cosa all'interno di un tag, puoi comunque guardare solo le stringhe. Usa il generatore di stringhe -

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

Per rimuovere spazi bianchi extra, usa il generatore di .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'

Salendo

In un'analogia con l '"albero genealogico", ogni tag e ogni stringa ha un genitore: il tag che lo contiene:

.genitore

Per accedere all'elemento genitore dell'elemento, utilizza l'attributo .parent.

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

Nel nostro html_doc, la stessa stringa del titolo ha un genitore: il tag <title> che lo contiene -

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

Il genitore di un tag di primo livello come <html> è l'oggetto Beautifulsoup stesso -

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

Il .parent di un oggetto Beautifulsoup è definito come Nessuno -

>>> print(soup.parent)
None

.parents

Per iterare su tutti gli elementi principali, utilizza l'attributo .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]

Andando di traverso

Di seguito è riportato un semplice documento:

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

Nel documento precedente, i tag <b> e <c> sono allo stesso livello e sono entrambi figli dello stesso tag. Entrambi i tag <b> e <c> sono fratelli.

.next_sibling e .previous_sibling

Usa .next_sibling e .previous_sibling per navigare tra gli elementi della pagina che si trovano sullo stesso livello dell'albero di analisi:

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

Il tag <b> ha un .next_sibling ma non .previous_sibling, poiché non c'è nulla prima del tag <b> sullo stesso livello dell'albero, lo stesso caso è con il tag <c>.

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

Le due stringhe non sono fratelli, poiché non hanno lo stesso genitore.

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

.next_siblings e .previous_siblings

Per scorrere i fratelli di un tag, usa .next_siblings e .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'

Andando avanti e indietro

Ora torniamo alle prime due righe nel nostro precedente esempio "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 parser HTML prende la stringa di caratteri sopra e la trasforma in una serie di eventi come "apri un tag <html>", "apri un tag <head>", "apri il tag <title>", "aggiungi una stringa", "Chiudi il tag </title>", "chiudi il tag </head>", "apri un tag <h4>" e così via. BeautifulSoup offre diversi metodi per ricostruire l'analisi iniziale del documento.

.next_element e .previous_element

L'attributo .next_element di un tag o di una stringa punta a tutto ciò che è stato analizzato immediatamente dopo. A volte sembra simile a .next_sibling, tuttavia non è del tutto uguale. Di seguito è riportato il tag <a> finale nel nostro documento di esempio "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.'

Tuttavia l'elemento .next_ di quel tag <a>, la cosa che è stata analizzata immediatamente dopo il tag <a>, non è il resto di quella frase: è la parola "C":

>>> last_a_tag.next_element
'C'

Il comportamento sopra è dovuto al fatto che nel markup originale, la lettera "C" appariva prima di quel punto e virgola. Il parser ha rilevato un tag <a>, quindi la lettera "C", quindi il tag di chiusura </a>, quindi il punto e virgola e il resto della frase. Il punto e virgola si trova allo stesso livello del tag <a>, ma prima è stata rilevata la lettera "C".

L'attributo .previous_element è l'esatto opposto di .next_element. Indica qualsiasi elemento è stato analizzato immediatamente prima di questo.

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

Usiamo questi iteratori per spostarci avanti e indietro su un elemento.

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