BeautifulSoup 教程, 在这一章中,我们将讨论按标签遍历的问题。
下面是我们的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')
>>>
基于上述文件,我们将尝试从文件的一个部分转移到另一个部分。
向下遍历
任何一篇HTML文档中的重要元素之一是标签,它可能包含其他标签/字符串(标签的子代)。BeautifulSoup提供了不同的方法来浏览和迭代标签的子代。
使用标签名进行遍历
搜索解析树的最简单方法是按标签的名称搜索。如果你想要<head>
标签,使用 soup.head −
>>> soup.head
<head>&t;title>Tutorials Point</title></head>
>>> soup.title
<title>Tutorials Point</title>
要获得<body>
标签中的特定标签(如第一个<b>
标签)。
>>> soup.body.b
<b>The Biggest Online Tutorials Library, It's all Free</b>
使用标签名作为属性,将只返回该名称的第一个标签。 −
>>> soup.a
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
要获得所有标签的属性,你可以使用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 和.children
我们可以通过标签的.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']
BeautifulSoup对象本身也有子女。在本例中,<html>
标签是BeautifulSoup对象的子代。 −
>>> len(soup.contents)
2
>>> soup.contents[1].name
'html'
一个字符串没有.contents,因为它不可能包含任何东西 −
>>> text = Ttag.contents[0]
>>> text.contents
self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'contents'
使用.children生成器来访问标签的子代,而不是以列表的形式获取它们 −
>>> for child in Ttag.children:
print(child)
Tutorials Point
.descendants
.descendants属性允许你遍历一个标签的所有子代 −
>>> for child in Htag.descendants:
print(child)
<title>Tutorials Point</title>
Tutorials Point
<head>
标签只有一个孩子,但它有两个后代:<title>
标签和<title>
标签的孩子。beautifulsoup对象只有一个直接的孩子(<html>
标签),但它有一大堆的后代 −
>>> len(list(soup.children))
2
>>> len(list(soup.descendants))
33
.string
如果标签只有一个子代,并且该子代是NavigableString,那么该子代将作为.string提供 −
>>> Ttag.string
'Tutorials Point'
如果一个标签的唯一子代是另一个标签,并且该标签有一个.string,那么父代标签就被认为与它的子代有相同的.string −
>>> Htag.contents
[<title>Tutorials Point</title>]
>>>
>>> Htag.string
'Tutorials Point'
然而,如果一个标签包含一个以上的东西,那么就不清楚.string应该指什么,所以.string被定义为None −
>>> print(soup.html.string)
None
.strings and stripped_strings
如果一个标签里面有不止一个东西,你仍然可以只看字符串。使用.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'
要删除多余的空白,请使用.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'
向上遍历
在一个 "家庭树 "的比喻中,每个标签和每个字符串都有一个父级:包含它的标签:
.parent
要访问该元素的父元素,使用.parent属性.
>>> Ttag = soup.title
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.parent
<head>title>Tutorials Point</title></head>
在我们的html_doc中,标题字符串本身有一个父级:包含它的<title>
标签−
>>> Ttag.string.parent
<title>Tutorials Point</title>
像<html>
这样的顶层标签的父级是Beautifulsoup对象本身。 −
>>> htmltag = soup.html
>>> type(htmltag.parent)
<class 'bs4.BeautifulSoup'>
Beautifulsoup对象的.parent被定义为None −
>>> print(soup.parent)
None
.parents
要遍历所有父元素,使用.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]
横向遍历
下面是一份简单的文档 −
>>> 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>
在上面的文档中, <b>
和<c>
标签处于同一级别,并且它们都是同一个标签的子代。. <b>
和<c>
标签都是兄弟姐妹关系.
.next_sibling 和 .previous_sibling
使用.next_sibling和.previous_sibling在解析树的同一层次上的页面元素之间进行遍历:
>>> 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>
<b>
标签有一个.next_sibling,但没有.previous_sibling,因为在<b>
标签之前,在同一层次的树上没有任何东西,<c>
标签的情况也是如此.
>>> print(sibling_soup.b.previous_sibling)
None
>>> print(sibling_soup.c.next_sibling)
None
这两个字符串不是兄弟姐妹,因为它们没有相同的父本。
>>> sibling_soup.b.string
'TutorialsPoint'
>>>
>>> print(sibling_soup.b.string.next_sibling)
None
.next_siblings 和 .previous_siblings
要迭代一个标签的兄弟姐妹,请使用.next_siblings和.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'
来回遍历
现在让我们回到之前 "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>
一个HTML解析器接收上述字符串,并将其转化为一系列事件,如 "打开一个<html>
标签"、"打开一个<head>
标签"、"打开<title>
标签"、"添加一个字符串"、"关闭</title>
标签"、"关闭</head>
标签"、"打开一个<h4>
标签 "等等。BeautifulSoup提供了不同的方法来重构文档的初始解析。
.next_element 和.previous_element
一个标签或字符串的.next_element属性指向紧随其后被解析的内容. 有时它看起来与.next_sibling相似,然而它并不完全相同. 下面是我们的 "html_doc "示例文档中的最后一个<a>
标签.
>>> 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.'
然而,那个<a>
标记的.next_element,即紧接在<a>
标记之后被解析的东西,不是那句话的其余部分:是单词 "C":
>>> last_a_tag.next_element
'C'
上述行为是因为在原始标记中,字母 "C "出现在该分号之前. 解析器遇到了一个<a>
标签,然后是字母 "C",然后是结尾的</a>
标签,然后是分号和句子的其余部分. 分号与<a>
标记在同一层次,但先遇到的是字母 "C".
.previous_element属性与.next_element完全相反。它指向紧接着这个元素之前被解析的任何元素.
>>> 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 和 .previous_elements
我们使用这些迭代器来向前和向后移动一个元素.
>>> 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'