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'