1、概述
在使用爬虫程序对爬取的文档进行处理时,经常要做的一个操作就是遍历文档树。文档以树形结构进行组织,所以遍历文档的操作又叫遍历文档树。beautiful soup本身提供了很多遍历文档树的方法,本文主要讨论遍历文档树的方法。
2、遍历文档树
2.1 准备工作
本文将使用公众号的文章作为遍历的对象,所以首先需要先把整个文档抓取下来,并将多余的元素去除掉,只保留文档的主体部分以保证文档分析的准确性,请参照如下代码:
from bs4 import BeautifulSoup
import requests
bk_url = 'https://mp.weixin.qq.com/s?src=11×tamp=1536542507&ver=1113&signature=bx7vKGc*P*VXdS7ymgZqblP666yq31soLbvKx1ehonqJAojCMT9aUq5y8Sv8CKTWd4C87pCCSw*kts5k4KaNUmdXuZkEMoVP59DPzKxQZhZlE6UCV3f3iw5qe5XvnJxs&new=1'
headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}
r = requests.get(bk_url,headers=headers)
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text,'html.parser')
2.2 获取子节点
在遍历文档树时一个经常性的操作就是获取子节点,我们可以使用下面几种方法来获取子节点。
2.2.1 使用节点名称获取子节点
可以使用节点名字直接获取相关节点,请参照如下代码
from bs4 import BeautifulSoup
import requests
bk_url = 'https://mp.weixin.qq.com/s?src=11×tamp=1536542507&ver=1113&signature=bx7vKGc*P*VXdS7ymgZqblP666yq31soLbvKx1ehonqJAojCMT9aUq5y8Sv8CKTWd4C87pCCSw*kts5k4KaNUmdXuZkEMoVP59DPzKxQZhZlE6UCV3f3iw5qe5XvnJxs&new=1'
headers = {"User-Agent" : "User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}
r = requests.get(bk_url,headers=headers)
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text,'html.parser')
body = soup.body
print(body)
2.2.2 .contents 和 .children
1、元素的 .contents 属性可以将tag的子节点以列表的方式输出,请参照如下代码:
head = soup.head
head.contents
注意:字符串没有 .contents 属性,因为字符串没有子节点
2、通过tag的 .children 生成器,可以对tag的子节点进行循环,请参照如下代码:
for child in head.children:
print(child)
2.2.3 .descendants
children与contents属性只能获取元素的直接子节点,但子节点的子节点是无法获取到的。如果需要获取某一个元素的所有子孙节点可以使用.descendants方法。
请参照如下代码:
for child in head.descendants:
print(child.name)
输出效果如下图所示:
2.2.4 .string
如果元素只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点,请参照如下代码:
print(head.title.string)
输出结果如下图所示:
如果一个元素仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None ,请参照如下代码:
print(head.string)
输出结果如下图所示:
2.2.5 .strings 和 stripped_strings
如果元素中包含多个字符串 ,可以使用 .strings 来循环获取,请参照如下代码:
for child_str in head.strings:
print(child_str)
输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容,全部是空格的行会被忽略掉,段首和段末的空白会被删除。请参照如下代码:
for child_str in head.stripped_strings:
print(child_str)
2.2.6 父节点
1) parent获取直接的父节点,请参考如下代码:
print(head.parent)
2) .parents 通过元素的 .parents 属性可以递归得到元素的所有父辈节点,下面的例子使用了 .parents 方法遍历了<a>标签到根节点的所有节点。
link = soup.a
link
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
# p
# body
# html
# [document]
# None
2.2.7 兄弟节点
下面的例子以下面的html为基础进行操作
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 属性来查询兄弟节点:
请参照如下代码:
sibling_soup.b.next_sibling
# <c>text2</c>
sibling_soup.c.previous_sibling
# <b>text1</b>
2).next_siblings 和 .previous_siblings
通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出:
请参照如下代码:
for sibling in soup.a.next_siblings:
print(repr(sibling))
# u',\n'
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
# u' and\n'
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
# u'; and they lived at the bottom of a well.'
# None
for sibling in soup.find(id="link3").previous_siblings:
print(repr(sibling))
# ' and\n'
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
# u',\n'
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
# u'Once upon a time there were three little sisters; and their names were\n'
# None