搜索文档树
示例文档:
>>> html_doc = """<html><head><title>睡鼠的故事</title></head>
<body>
<p class="title"><b>睡鼠的故事</b></p>
<p class="story">从前有三位小姐姐,她们的名字是:
<a href="http://example.com/elsie" class="sister" id="link1">埃尔西</a>,
<a href="http://example.com/lacie" class="sister" id="link2">莱斯</a>和
<a href="http://example.com/tillie" class="sister" id="link3">蒂尔莉</a>;
她们住在一个井底下面。</p>
<p class="story">...</p>
"""
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(html_doc, 'html.parser')
几种过滤器
以下的过了长期贯穿了所有的搜索API函数,它们可以被使用在标签的名称、属性和文本这些上面
字符串
最简单的过滤器就是字符串,在搜索方法中传入一个字符串参数,BeautifulSoup
会查找与字符串匹配的内容,下面的例子用于查找文档中的所有的<b>
标签:
>>> soup.find('b')
<b>睡鼠的故事</b>
如果传入的是字节码参数,BeautifulSoup
将会假设这个编码为UTF-8编码,为了避免出错,可以直接传入一个Unicode编码
正则表达式
如果传入正则表达式作为参数,BeautifulSoup
将会以正则表达式的match()
方法来匹配内容。下面的示例中将找出所有以b
开头的标签:
>>> import re
>>> for tag in soup.find_all(re.compile("^b")):
print(tag.name)
body
b
列表
如果传入列表参数,BeautifulSoup
会将与列表中的任意元素进行匹配,返回匹配的内容。。下面的代码可以找到文档中所有的<a>
和<b>
标签:
>>> soup.find_all(["a", "b"])
[<b>睡鼠的故事</b>, <a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
True
Ture
值可以匹配任意值,下面代码查找所有的标签,但是不会返回字符串的节点:
>>> for tag in soup.find_all(True):
print(tag.name)
html
head
title
body
p
b
p
a
a
a
p
函数
如果没有适合的过滤器,可以自己定义一个函数,**该函数只接受一个元素作为参数**。如果这个方法返回`True`表示当前元素匹配并且被找到,否则返回`False`
下面这个函数用于匹配那些包含class
属性但不包含id
属性的标签:
>>> def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
将这个函数作为参数传入find_all()
方法,将得到所有的<p>
标签:
>>> soup.find_all(has_class_but_no_id)
[<p class="title"><b>睡鼠的故事</b></p>, <p class="story">从前有三位小姐姐,她们的名字是:
<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>,
<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>和
<a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>;
她们住在一个井底下面。</p>, <p class="story">...</p>]
返回结果中只有<p>
标签没有<a>
标签(上面出现的<a>
是包含在<p>
中的),因为<a>
标签中还定义了id
,没有返回<html>
和<head>
,因为<html>
和<head>
没有class
属性。
如果传入一个函数来过滤一个像href
这样的特定属性,传入函数的参数将式属性值,而不是整个标签。下面的函数可以找到所有拥有href
属性但不包含lacie
字符串的标签:
>>> def not_lacie(href):
return href and not re.compile("lacie").search(href)
>>> soup.find_all(href=not_lacie)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
find_all()
find_all(name, attrs, recrursive, string, limit, **kwargs)
find_all()
方法搜索当前tag下的所有子节点,并判断是否符合过滤器的条件。
name
参数
通过name参数,可以指定名字来查找标签。最简单的用法如下:
>>> soup.find_all("title")
[<title>睡鼠的故事</title>]
前面提过的过滤器的均可作为name参数的值:字符串、正则表达式、列表、函数或者一个布尔类型的值True
keyword参数
如果一个指定名字的参数不是搜索内置的(name, attrs, recursive, string, limit)
参数名,搜索时会把该参数当做指定tag的属性来搜索。
比如传入一个名为id
的参数,BeautifulSoup
将会搜索每一个tag的id
属性:
>>> soup.find_all(id="link2")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
如果传入一个名为href
的参数,BeautifulSoup
将会搜索每个tag的href
属性:
>>> soup.find_all(href=re.compile("elsie"))
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>]
搜索指定名字的属性时可以使用的参数值包括:字符串、正则表达式、列表、函数和True
值
下面的例子在文档树中查找所有包含id
属性的tag
,无论id是什么值都会匹配:
>>> soup.find_all(id=True)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
注意:有些tag属性在搜索中不能使用,比如HTML5中的data-*
属性。但是可以通过将这些属性放入一个字典中,然后传入attrs
关键字参数来实现:
>>> data_soup = BeautifulSoup('<div data-foo="value">foo!</div>', "html.parser")
>>> data_soup.find_all(data-foo="value")
SyntaxError: keyword can't be an expression
>>> data_soup.find_all(attrs={"data-foo": "value"})
[<div data-foo="value">foo!</div>]
根据CSS进行搜索
按照CSS类名搜索标签的功能非常实用,但由于表示CSS类名的关键字class
在Python中是保留字,所以使用class作为参数会出错,在BS中可以通过class_
参数搜索有指定CSS类名的标签:
>>> soup.find_all("a", class_="sister")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
根关键字参数一样,class_
参数也支持不同类型的过滤器:字符串、正则表达式、函数或者True
:
>>> soup.find_all(class_=re.compile("itl"))
[<p class="title"><b>睡鼠的故事</b></p>]
>>>
>>> def has_six_characters(css_class):
return css_class is not None and len(css_class) == 6
>>> soup.find_all(class_=has_six_characters)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
注意:
-
标签的
class
属性支持同时有多个值,按照CSS类名搜索标签时,可以分别搜索表中的每一个CSS类名。 -
搜索class属性时也可以指定完全匹配的CSS值
-
如果CSS的值与文档的不一致,将会导致结果搜索不到
-
如果希望搜索结果同时匹配两个以上的CSS类名,应该使用CSS选择器:
>>> css_soup.select("p.strikeout.body") [<p class="body strikeout"></p>]
string
参数
通过string
参数可以搜索标签中的文本标签。与name
参数一样,string
参数接受字符串、正则表达式、列表、函数或者直接一个布尔类型值True
示例:
>>> soup.find_all(string="埃尔西")
['埃尔西']
>>>
>>> soup.find_all(string=["蒂尔莉", "埃尔西", "莱斯"])
['埃尔西', '莱斯', '蒂尔莉']
>>>
>>> soup.find_all(string=re.compile("睡鼠"))
['睡鼠的故事', '睡鼠的故事']
>>>
>>> def is_the_only_string_within_a_tag(s):
"""如果字符串是其父标签的唯一子节点,则返回 True。"""
return (s == s.parent.string)
>>> soup.find_all(string=is_the_only_string_within_a_tag)
['睡鼠的故事', '睡鼠的故事', '埃尔西', '莱斯', '蒂尔莉', '...']
尽管string
参数是用于搜索字符串的,但是可以与其他的参数混合起来使用,下面的代码BS会找到所有与string参数值相匹配的<a>
标签:
>>> soup.find_all("a", string="埃尔西")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>]
limit 参数
find_all()
方法返回匹配过滤器的所有标签和文本,如果文档树很大,搜索就会变得很慢,如果不想要全部的结果,可以使用limit
参数进行限制返回结果的数量,当搜索到的结果数量达到了limit的限制时,就停止搜索并返回结果。
文档树中有3个标签符合搜索结果,但是结果只返回2个,因为设置了limit参数:
>>> soup.find_all("a", limit=2)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
recursive 参数
如果调用mytag.find_all()
方法,BS将会获取mytag
的所有子孙节点,但是如果只是想搜索mytag
的直接子节点,可以使用recursive=False
参数,对比一下:
>>> soup.html.find_all("title")
[<title>睡鼠的故事</title>]
>>>
>>> soup.html.find_all("title", recursive=False)
[]
像调用find_all()
一样调用一个标签
由于 find_all()
几乎是 BeautifulSoup
中最常用的搜索方法,所以我们为它定义了一种简写的形式:如果你将 BeautifulSoup
对象或 Tag 对象当作一个方法来使用,那么这个方法的执行结果与调用这个对象的 find_all()
方法是相同的。
示例:
以下的代码是等价的:
soup.find_all("a")
soup("a")
以下的代码也是等价的:
soup.title.find_all(text=True)
soup.title(text=True)
find() 方法
find_all(name, attrs, recursive, string, \**kwargs)
find_all()
方法返回结果是一个列表,而find()
方法是直接返回结果。find_all()
方法没有找到目标返回的是空列表,find()
方法没有找到就返回None。
CSS选择器
BeautifulSoup
中有一个select()
方法,该方法使用SoupSieve
对解析的文档运行CSS选择器并返回所有匹配的元素。
可以使用CSS选择器的语法找到Tag:
>>> soup.select("title")
[<title>睡鼠的故事</title>]
>>> soup.select("p:nth-of-type(3)")
[<p class="story">...</p>]
通过tag标签逐层查找:
>>> soup.select("body a")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("html head title")
[<title>睡鼠的故事</title>]
找到某个tag标签下的直接字标签:
>>> soup.select("head > title")
[<title>睡鼠的故事</title>]
>>> soup.select("p > a")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("p > a:nth-of-type(2)")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
>>> soup.select("p > #link1")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>]
>>> soup.select("body > a")
[]
找到兄弟节点:
>>> soup.select("#link1 ~ .sister")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("#link1 + .sister")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
通过CSS的类名查找:
>>> soup.select(".sister")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("[class~=sister]")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
通过tag的ID查找:
>>> soup.select("#link1")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>]
>>> soup.select("a#link2")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
查找与选择器列表中的任何选择器匹配的tag:
>>> soup.select("#link1,#link2")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
网友评论