遍历文档树
Posted lalavender
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了遍历文档树相关的知识,希望对你有一定的参考价值。
子节点
一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.
注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点
1. tag的名字
操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取
<head>
标签,只要用 soup.head :这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法.下面的代码可以获取
<body>
标签中的第一个<b>
标签:通过点取属性的方式只能获得当前名字的第一个tag:
如果想要得到所有的
<a>
标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到搜索文档树中描述的方法,比如:find_all()
2. .contents 和 .children
tag的
.contents
属性可以将tag的子节点以列表的方式输出:BeautifulSoup 对象本身一定会包含子节点,也就是说
<html>
标签也是 BeautifulSoup 对象的子节点:字符串没有 .contents 属性,因为字符串没有子节点:
通过tag的 .children 生成器,可以对tag的子节点进行循环:
3. .descendants
.contents
和.children
属性仅包含tag的直接子节点.例如,<head>
标签只有一个直接子节点<title>
head_tag.contents
# [<title>The Dormouse's story</title>]
- 但是
标签也包含一个子节点:字符串 “The Dormouse’s story”,这种情况下字符串 “The Dormouse’s story”也属于 <head>
标签的子孙节点..descendants
属性可以对所有tag的子孙节点进行递归循环(先序遍历):
for child in head_tag.descendants:
print(child)
# <title>The Dormouse's story</title>
# The Dormouse's story
上面的例子中, <head>
标签只有一个子节点,但是有2个子孙节点:<head>
节点和<head>
的子节点, BeautifulSoup 有一个直接子节点(<html>
节点),却有很多子孙节点:
len(list(soup.children))
# 1
len(list(soup.descendants))
# 25
4. .string
- 如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用
.string
得到子节点:
title_tag.string
# u'The Dormouse's story'
如果一个tag仅有一个子节点,那么这个tag也可以使用 .string
方法,输出结果与当前唯一子节点的 .string
结果相同:
head_tag.contents
# [<title>The Dormouse's story</title>]
head_tag.string
# u'The Dormouse's story'
如果tag包含了多个子节点,tag就无法确定 .string
方法应该调用哪个子节点的内容, .string
的输出结果是 None :
print(soup.html.string)
# None
5. .strings 和 stripped_strings
父节点
- 每个tag或字符串都有父节点:被包含在某个tag中
1. .parent
通过
.parent
属性来获取某个元素的父节点.在例子文档中,<head>
标签是<title>
标签的父节点:文档title的字符串也有父节点:
标签 文档的顶层节点比如
<html>
的父节点是 BeautifulSoup 对象:BeautifulSoup 对象的
.parent
是None:
2. .parents
- 通过元素的 .parents 属性可以递归得到元素的所有父辈节点,下面的例子使用了 .parents 方法遍历了
<a>
标签到根节点的所有节点
兄弟节点
sibling_soup = BeautifulSoup("<a><b>text1</b><c>text2</c></b></a>")
print(sibling_soup.prettify())
# <html>
# <body>
# <a>
# <b>
# text1
# </b>
# <c>
# text2
# </c>
# </a>
# </body>
# </html>
因为标签和
标签是同一层:他们是同一个元素的子节点,所以和 可以被称为兄弟节点.一段文档以标准格式输出时,兄弟节点有相同的缩进级别.在代码中也可以使用这种关系.
1. .next_sibling 和 .previous_sibling
在文档树中,使用
.next_sibling
和.previous_sibling
属性来查询兄弟节点:例子中的字符串“text1”和“text2”不是兄弟节点,因为它们的父节点不同:
实际文档中的tag的
.next_sibling
和.previous_sibling
属性通常是字符串或空白.
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
如果以为第一个
<a>
标签的.next_sibling
结果是第二个<a>
标签,那就错了,真实结果是第一个<a>
标签和第二个<a>
标签之间的顿号和换行符:
第二个<a>
标签是顿号的 .next_sibling
属性:
2. .next_siblings 和 .previous_siblings
- 通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出:
回退和前进
<html><head><title>The Dormouse's story</title></head>
<p class="title"><b>The Dormouse's story</b></p>
HTML解析器把这段字符串转换成一连串的事件: “打开标签”,”打开一个
标签”,”打开一个标签”,”添加一段字符串”,”关闭 标签”,”打开 标签”,等等.Beautiful Soup提供了重现解析器初始化过程的方法.
1. .next_element 和 .previous_element
.next_element
属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与.next_sibling
相同,但通常是不一样的.
这是文档中最后一个
<a>
标签,它的.next_sibling
结果是一个字符串,因为当前的解析过程 [2] 因为当前的解析过程因为遇到了<a>
标签而中断了:
last_a_tag = soup.find("a", id="link3")
last_a_tag
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
last_a_tag.next_sibling
# '; and they lived at the bottom of a well.'
但这个
<a>
标签的.next_element
属性结果是在<a>
标签被解析之后的解析内容,不是<a>
标签后的句子部分,应该是字符串”Tillie”:
last_a_tag.next_element
# u'Tillie'
这是因为在原始文档中,字符串“Tillie” 在分号前出现,解析器先进入
<a>
标签,然后是字符串“Tillie”,然后关闭</a>
标签,然后是分号和剩余部分.分号与<a>
标签在同一层级,但是字符串“Tillie”会被先解析.
.previous_element
属性刚好与.next_element
相反,它指向当前被解析的对象的前一个解析对象:
2. .next_elements 和 .previous_elements
- 通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样:
以上是关于遍历文档树的主要内容,如果未能解决你的问题,请参考以下文章
BeautifulSoup文档3-详细方法 | 如何对文档树进行遍历?
BeautifulSoup文档3-详细方法 | 如何对文档树进行遍历?