Piękna zupa - nawigacja za pomocą tagów
W tym rozdziale omówimy nawigację za pomocą tagów.
Poniżej znajduje się nasz dokument 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')
>>>
Na podstawie powyższego dokumentu spróbujemy przejść z jednej części dokumentu do drugiej.
Zejście w dół
Jednym z ważnych elementów każdego fragmentu dokumentu HTML są tagi, które mogą zawierać inne tagi / ciągi znaków (dzieci tagu). Beautiful Soup zapewnia różne sposoby nawigacji i iteracji po elementach potomnych tagu.
Nawigacja przy użyciu nazw tagów
Najłatwiejszym sposobem przeszukania drzewa parsowania jest wyszukanie znacznika według jego nazwy. Jeśli chcesz mieć tag <head>, użyj soup.head -
>>> soup.head
<head>&t;title>Tutorials Point</title></head>
>>> soup.title
<title>Tutorials Point</title>
Aby uzyskać określony tag (taki jak pierwszy tag <b>) w tagu <body>.
>>> soup.body.b
<b>The Biggest Online Tutorials Library, It's all Free</b>
Użycie nazwy tagu jako atrybutu da ci tylko pierwszy tag o tej nazwie -
>>> soup.a
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
Aby uzyskać wszystkie atrybuty tagu, możesz użyć metody 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 i .children
Możemy wyszukiwać dzieci tagu na liście według jego .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']
Sam obiekt BeautifulSoup ma dzieci. W tym przypadku tag <html> jest elementem podrzędnym obiektu BeautifulSoup -
>>> len(soup.contents)
2
>>> soup.contents[1].name
'html'
Ciąg nie zawiera .contents, ponieważ nie może niczego zawierać -
>>> text = Ttag.contents[0]
>>> text.contents
self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'contents'
Zamiast pobierać je jako listę, użyj generatora .children, aby uzyskać dostęp do dzieci tagu -
>>> for child in Ttag.children:
print(child)
Tutorials Point
.potomków
Atrybut .descendants umożliwia iterację po wszystkich elementach podrzędnych tagu, rekurencyjnie -
jego bezpośrednie dzieci i dzieci jego bezpośrednich dzieci i tak dalej -
>>> for child in Htag.descendants:
print(child)
<title>Tutorials Point</title>
Tutorials Point
Tag <head> ma tylko jedno dziecko, ale ma dwóch potomków: tag <title> i tag podrzędny tagu <title>. Obiekt beautifulsoup ma tylko jedno bezpośrednie dziecko (tag <html>), ale ma wiele potomków -
>>> len(list(soup.children))
2
>>> len(list(soup.descendants))
33
.strunowy
Jeśli tag ma tylko jedno dziecko, a to dziecko jest NavigableString, element podrzędny jest udostępniany jako .string -
>>> Ttag.string
'Tutorials Point'
Jeśli jedynym elementem podrzędnym tagu jest inny tag, a ten tag ma .string, to uznaje się, że tag nadrzędny ma taki sam .string, jak jego element podrzędny -
>>> Htag.contents
[<title>Tutorials Point</title>]
>>>
>>> Htag.string
'Tutorials Point'
Jeśli jednak tag zawiera więcej niż jedną rzecz, nie jest jasne, do czego ma się odnosić .string, więc .string jest zdefiniowane jako None -
>>> print(soup.html.string)
None
.strings i stripped_strings
Jeśli w tagu jest więcej niż jedna rzecz, nadal możesz patrzeć tylko na ciągi. Użyj generatora .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'
Aby usunąć dodatkowe spacje, użyj generatora .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'
Wchodzę w górę
W analogii „drzewo genealogiczne” każdy znacznik i każdy ciąg ma rodzica: znacznik, który go zawiera:
.rodzic
Aby uzyskać dostęp do elementu nadrzędnego elementu, użyj atrybutu .parent.
>>> Ttag = soup.title
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.parent
<head>title>Tutorials Point</title></head>
W naszym html_doc sam ciąg tytułu ma rodzica: znacznik <title>, który go zawiera
>>> Ttag.string.parent
<title>Tutorials Point</title>
Rodzicem tagu najwyższego poziomu, takiego jak <html>, jest sam obiekt Beautifulsoup -
>>> htmltag = soup.html
>>> type(htmltag.parent)
<class 'bs4.BeautifulSoup'>
Element .parent obiektu Beautifulsoup jest zdefiniowany jako None -
>>> print(soup.parent)
None
.rodzice
Aby iterować po wszystkich elementach nadrzędnych, użyj atrybutu .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]
Idąc bokiem
Poniżej znajduje się jeden prosty dokument -
>>> 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>
W powyższym dokumencie znaczniki <b> i <c> są na tym samym poziomie i oba są elementami podrzędnymi tego samego znacznika. Zarówno tag <b>, jak i <c> są rodzeństwem.
.next_sibling i .previous_sibling
Użyj .next_sibling i .previous_sibling, aby nawigować między elementami strony, które znajdują się na tym samym poziomie drzewa parsowania:
>>> 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>
Znacznik <b> ma .next_sibling, ale nie ma .previous_sibling, ponieważ nie ma nic przed znacznikiem <b> na tym samym poziomie drzewa, to samo dotyczy tagu <c>.
>>> print(sibling_soup.b.previous_sibling)
None
>>> print(sibling_soup.c.next_sibling)
None
Te dwa łańcuchy nie są rodzeństwem, ponieważ nie mają tego samego rodzica.
>>> sibling_soup.b.string
'TutorialsPoint'
>>>
>>> print(sibling_soup.b.string.next_sibling)
None
.next_siblings i .previous_siblings
Aby iterować po rodzeństwie tagu, użyj .next_siblings i .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'
Chodzić tam i z powrotem
Wróćmy teraz do pierwszych dwóch wierszy w naszym poprzednim przykładzie „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>
Parser HTML bierze powyższy ciąg znaków i zamienia go w serię zdarzeń, takich jak „otwórz tag <html>”, „otwórz tag <head>”, „otwórz tag <title>”, „dodaj ciąg”, „Zamknij tag </title>”, „zamknij tag </head>”, „otwórz tag <h4>” i tak dalej. BeautifulSoup oferuje różne metody rekonstrukcji początkowej analizy dokumentu.
.next_element i .previous_element
Atrybut .next_element tagu lub ciągu wskazuje na wszystko, co zostało przeanalizowane natychmiast po tym. Czasami wygląda podobnie do .next_sibling, jednak nie jest całkowicie taki sam. Poniżej znajduje się ostatni znacznik <a> w naszym przykładowym dokumencie „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.'
Jednak .next_element tego znacznika <a>, rzecz, która została przeanalizowana bezpośrednio po znaczniku <a>, nie jest resztą tego zdania: jest to słowo „C”:
>>> last_a_tag.next_element
'C'
Powyższe zachowanie wynika z tego, że w oryginalnym oznaczeniu litera „C” pojawiła się przed średnikiem. Parser napotkał znacznik <a>, następnie literę „C”, następnie tag zamykający </a>, a następnie średnik i resztę zdania. Średnik znajduje się na tym samym poziomie co znacznik <a>, ale najpierw napotkano literę „C”.
Atrybut .previous_element jest dokładnym przeciwieństwem .next_element. Wskazuje na dowolny element, który został przeanalizowany bezpośrednio przed tym.
>>> 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 i .previous_elements
Używamy tych iteratorów, aby przejść do przodu i do tyłu do elementu.
>>> 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'