Sopa Linda - Navegando por Tags

Neste capítulo, discutiremos sobre como navegar por tags.

Abaixo está o nosso 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')
>>>

Com base no documento acima, tentaremos passar de uma parte do documento para outra.

Indo para baixo

Uma das partes importantes do elemento em qualquer parte do documento HTML são as tags, que podem conter outras tags / strings (filhos das tags). Beautiful Soup fornece diferentes maneiras de navegar e iterar os filhos da tag.

Navegar usando nomes de tag

A maneira mais fácil de pesquisar uma árvore de análise é pesquisar a marca por seu nome. Se você quiser a tag <head>, use soup.head -

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

Para obter uma tag específica (como a primeira <b> tag) na tag <body>.

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

Usar um nome de tag como um atributo fornecerá a você apenas a primeira tag com esse nome -

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

Para obter todos os atributos da tag, você pode usar o método 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 e .children

Podemos pesquisar os filhos da tag em uma lista por seus .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']

O próprio objeto BeautifulSoup tem filhos. Nesse caso, a tag <html> é filha do objeto BeautifulSoup -

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

Uma string não tem .contents, porque não pode conter nada -

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

Em vez de obtê-los como uma lista, use o gerador .children para acessar os filhos da tag -

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

.descendants

O atributo .descendants permite que você itere sobre todos os filhos de uma tag, recursivamente -

seus filhos diretos e os filhos de seus filhos diretos e assim por diante -

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

A tag <head> tem apenas um filho, mas tem dois descendentes: a tag <title> e a criança da tag <title>. O objeto beautifulsoup tem apenas um filho direto (a tag <html>), mas tem muitos descendentes -

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

.corda

Se a tag tiver apenas um filho e esse filho for um NavigableString, o filho será disponibilizado como .string -

>>> Ttag.string
'Tutorials Point'

Se o único filho de uma tag for outra tag e essa tag tiver um .string, a tag pai será considerada como tendo o mesmo .string que seu filho -

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

No entanto, se uma tag contém mais de uma coisa, não está claro a que .string deve se referir, então .string é definida como Nenhum -

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

.strings e stripped_strings

Se houver mais de uma coisa dentro de uma tag, você ainda pode olhar apenas as strings. Use o gerador .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'

Para remover espaços em branco extras, use o gerador .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'

Subindo

Em uma analogia com a “árvore genealógica”, cada tag e cada string tem um pai: a tag que o contém:

.parent

Para acessar o elemento pai do elemento, use o atributo .parent.

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

Em nosso html_doc, a string de título em si tem um pai: a tag <title> que a contém−

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

O pai de uma tag de nível superior como <html> é o próprio objeto Beautifulsoup -

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

O .parent de um objeto Beautifulsoup é definido como Nenhum -

>>> print(soup.parent)
None

.pais

Para iterar sobre todos os elementos pais, use o atributo .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]

Indo de lado

Abaixo está um documento simples -

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

No documento acima, as tags <b> e <c> estão no mesmo nível e são filhas da mesma tag. Ambas as tags <b> e <c> são irmãs.

.next_sibling e .previous_sibling

Use .next_sibling e .previous_sibling para navegar entre os elementos da página que estão no mesmo nível da árvore de análise:

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

A tag <b> tem um .next_sibling, mas nenhum .previous_sibling, pois não há nada antes da tag <b> no mesmo nível da árvore, mesmo caso é com a tag <c>.

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

As duas strings não são irmãs, pois não têm o mesmo pai.

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

.next_siblings e .previous_siblings

Para iterar os irmãos de uma tag, use .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'

Indo e voltando

Agora, vamos voltar às duas primeiras linhas em nosso exemplo anterior “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>

Um analisador HTML pega a string de caracteres acima e a transforma em uma série de eventos como “abrir uma tag <html>”, “abrir uma tag <head>”, “abrir a tag <title>”, “adicionar uma string”, “Feche a tag </title>”, “feche a tag </head>”, “abra uma tag <h4>” e assim por diante. BeautifulSoup oferece diferentes métodos para reconstruir a análise inicial do documento.

.next_element e .previous_element

O atributo .next_element de uma tag ou string aponta para tudo o que foi analisado imediatamente depois. Às vezes, é semelhante a .next_sibling, mas não é totalmente igual. Abaixo está a tag <a> final em nosso documento de exemplo “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.'

No entanto, o .next_element dessa tag <a>, o que foi analisado imediatamente após a tag <a>, não é o resto da frase: é a palavra “C”:

>>> last_a_tag.next_element
'C'

O comportamento acima ocorre porque na marcação original, a letra “C” apareceu antes desse ponto-e-vírgula. O analisador encontrou uma marca <a>, depois a letra “C”, depois a marca de fechamento </a>, depois o ponto-e-vírgula e o resto da frase. O ponto-e-vírgula está no mesmo nível da tag <a>, mas a letra “C” foi encontrada primeiro.

O atributo .previous_element é exatamente o oposto de .next_element. Ele aponta para qualquer elemento analisado imediatamente antes deste.

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

Usamos esses iteradores para avançar e voltar para um 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'