上回学到了遍历文档树,现在来通过“爱丽丝梦游仙境”这段例子学习从文档的一段内容找另一段内容。
html_doc =
"""<html><head><title>The Dormouse's story</title></head>
<body>
<p class= "title"><b>The Dormouse's story</b></p>
<p class = "story>Once upon a time there were three little sister; and their names were
<a href = "http://example.com/elisie" class = "sister" id = "link1">Elisie</a>
<a href = "http://example.com/lacie" class = "sister" id = "link2">Lacie</a> and
<a href = "http://example.com/tillie" class = "sister" id = "link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class = "story">...</p>"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc,"html.parser")
子节点
一个Tag可能包含多个字符串或其他的Tag,这些都是这个Tag的子节点。BeautifulSoup提供了许多操作和遍历子节点的属性。
注意:BeautifulSoup中字符串节点不支持这些属性,因为字符串没有子节点
tag的名字
操作文档树最简单的方法就是告诉它你想获取的tag的name。如果想获取<head>标签,只要用soup.head
soup.head
# <head><title>The Dormouse's story</title></head>
soup.title
# <title>The Dormouse's story</title>
这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法。下面的代码可以获取<body>标签中的第一个<b>标签:
soup.body.b
# <b>The Dormouse's story</b>
通过点取属性的方式只能获得当前名字的第一个tag:
soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
如果想要得到所有的<a>标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到Search the tree中描述的方法。比如:find_all()或findAll()
soup.findAll('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
findAll()将会返回一个列表
.contents和.children
tag的.contents属性可以将tag的子节点以列表的方式输,标签包含的字符串是标签本身的子节点:
head_tag = soup.head
head_tag
# <head><title>The Dormouse's story</title></head>
head_tag.contents
# [<title>The Dormouse's story</title>]
head_tag.contents[0]
# <title>The Dormouse's story</title>
head_tag.contents[1]
# The Dormouse's story
html标签是BeautifulSoup对象的子节点,字符串没有子节点,所以它没有.contents属性:
len(soup.contents)
# 1
soup.contents[0].name
# html
text = soup.title.contents[0]
text.contents
# AttributeError: 'NavigableString' object has no attribute 'contents'
通过tag的.children生成器,可以对tag的子节点进行循环:
for child in soup.title.children:
print(child)
# The Dormouse's story
子孙节点 .descendants
.contents和.children属性仅包含了tag的直接子节点。例如,<head>标签只有一个直接子节点<title>
head_tag.contents
# [<title>The Dormouse's story</title>]
但是<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个子孙节点:<title>节点和<title>的子节点,BeautifulSoup有一个直接子节点(html节点),却又很多子孙节点:
len(list(soup.children))
# 1
len(list(soup.descendants))
# 25