来源:官方文档 http://beautifulsoup.readthedocs.io/zh_CN/latest/
如何使用
将一段文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象, 可以传入一段字符串或一个文件句柄.
首先,文档被转换成Unicode,并且HTML的实例都被转换成Unicode编码
对象的种类
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag
, NavigableString
, BeautifulSoup
, Comment
.
Tag
Tag
对象与XML或HTML原生文档中的tag(即标签)相同:
Tag有很多方法和属性,在 遍历文档树 和 搜索文档树 中有详细解释.现在介绍一下tag中最重要的属性: name和attributes
Name
每个tag都有自己的名字,通过 .name
来获取:
如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
Attributes
一个tag可能有很多个属性. tag <b class="boldest">
有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:
也可以直接”点”取属性, 比如: .attrs
:
tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样
可以遍历的字符串
字符串常被包含在tag内.Beautiful Soup用 NavigableString
类来包装tag中的字符串:
一个 NavigableString
字符串与Python中的Unicode字符串相同,并且还支持包含在 遍历文档树 和 搜索文档树 中的一些特性. 通过 unicode()
方法可以直接将 NavigableString
对象转换成Unicode字符串:
tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 replace_with() 方法:
NavigableString
对象支持 遍历文档树 和 搜索文档树 中定义的大部分属性, 并非全部.尤其是,一个字符串不能包含其它内容(tag能够包含字符串或是其它tag),字符串不支持.contents
或 .string
属性或 find()
方法.
如果想在Beautiful Soup之外使用 NavigableString
对象,需要调用 unicode()
方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存.
BeautifulSoup
BeautifulSoup
对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag
对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup
对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name
属性是很方便的,所以 BeautifulSoup
对象包含了一个值为 “[document]” 的特殊属性 .name
注释及特殊字符串
Tag
, NavigableString
, BeautifulSoup
几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:
Comment
对象是一个特殊类型的 NavigableString
对象:
但是当它出现在HTML文档中时, Comment
对象会使用特殊的格式输出:
tag的名字
操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取 <head> 标签,只要用 soup.head
:
这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法.下面的代码可以获取<body>标签中的第一个<b>标签:
通过点取属性的方式只能获得当前名字的第一个tag:
如果想要得到所有的<a>标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到Searching the tree 中描述的方法,比如: find_all()
.contents 和 .children
tag的 .contents
属性可以将tag的子节点以列表的方式输出:
BeautifulSoup
对象本身一定会包含子节点,也就是说<html>标签也是 BeautifulSoup
对象的子节点:
字符串没有 .contents
属性,因为字符串没有子节点:
通过tag的 .children
生成器,可以对tag的子节点进行循环:
.descendants
.contents
和 .children
属性仅包含tag的直接子节点.例如,<head>标签只有一个直接子节点<title>
但是<title>标签也包含一个子节点:字符串 “The Dormouse’s story”,这种情况下字符串 “The Dormouse’s story”也属于<head>标签的子孙节点. .descendants
属性可以对所有tag的子孙节点进行递归循环 [5] :
上面的例子中, <head>标签只有一个子节点,但是有2个子孙节点:<head>节点和<head>的子节点, BeautifulSoup
有一个直接子节点(<html>节点),却有很多子孙节点:
.string
如果tag只有一个 NavigableString
类型子节点,那么这个tag可以使用 .string
得到子节点:
如果一个tag仅有一个子节点,那么这个tag也可以使用 .string
方法,输出结果与当前唯一子节点的 .string
结果相同:
如果tag包含了多个子节点,tag就无法确定 .string
方法应该调用哪个子节点的内容,.string
的输出结果是 None
:
搜索文档树
Beautiful Soup定义了很多搜索方法,这里着重介绍2个: find()
和 find_all()
.其它方法的参数和用法类似,请读者举一反三.
再以“爱丽丝”文档作为例子:
使用 find_all()
类似的方法可以查找到想要查找的文档内容
过滤器
介绍 find_all()
方法前,先介绍一下过滤器的类型 [3] ,这些过滤器贯穿整个搜索的API.过滤器可以被用在tag的name中,节点的属性中,字符串中或他们的混合中.
字符串
最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的<b>标签:
如果传入字节码参数,Beautiful Soup会当作UTF-8编码,可以传入一段Unicode 编码来避免Beautiful Soup解析编码出错
正则表达式
如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match()
来匹配内容.下面例子中找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到:
下面代码找出所有名字中包含”t”的标签:
使用find_all经常需要配合 for 循环
列表
如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
find_all()
find_all( name , attrs , recursive , string , **kwargs )
find_all()
方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.这里有几个例子:
有几个方法很相似,还有几个方法是新的,参数中的 string
和 id
是什么含义? 为什么find_all("p", "title")
返回的是CSS Class为”title”的<p>标签? 我们来仔细看一下find_all()
的参数
name 参数
name
参数可以查找所有名字为 name
的tag,字符串对象会被自动忽略掉.
简单的用法如下:
重申: 搜索 name
参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是True
.
keyword 参数
如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id
的参数,Beautiful Soup会搜索每个tag的”id”属性.
如果传入 href
参数,Beautiful Soup会搜索每个tag的”href”属性:
搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True .
下面的例子在文档树中查找所有包含 id
属性的tag,无论 id
的值是什么:
使用多个指定名字的参数可以同时过滤tag的多个属性:
有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
但是可以通过 find_all()
方法的 attrs
参数定义一个字典参数来搜索包含特殊属性的tag:
按CSS搜索
按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class
在Python中是保留字,使用 class
做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_
参数搜索有指定CSS类名的tag:
class_
参数同样接受不同类型的 过滤器
,字符串,正则表达式,方法或 True
:
tag的 class
属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名:
搜索 class
属性时也可以通过CSS值完全匹配:
完全匹配 class
的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:
string
参数
通过 string
参数可以搜搜文档中的字符串内容.与 name
参数的可选值一样, string
参数接受 字符串 , 正则表达式 , 列表, True . 看例子:
虽然 string
参数用于搜索字符串,还可以与其它参数混合使用来过滤tag.Beautiful Soup会找到 .string
方法与 string
参数值相符的tag.下面代码用来搜索内容里面包含“Elsie”的<a>标签:
limit
参数
find_all()
方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit
参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit
的限制时,就停止搜索返回结果.
文档树中有3个tag符合搜索条件,但结果只返回了2个,因为我们限制了返回数量:
recursive
参数
调用tag的 find_all()
方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False
.
一段简单的文档:
是否使用 recursive
参数的搜索结果:
这是文档片段
<title>标签在 <html> 标签下, 但并不是直接子节点, <head> 标签才是直接子节点. 在允许查询所有后代节点时 Beautiful Soup 能够查找到 <title> 标签. 但是使用了 recursive=False
参数之后,只能查找直接子节点,这样就查不到 <title> 标签了.
Beautiful Soup 提供了多种DOM树搜索方法. 这些方法都使用了类似的参数定义. 比如这些方法: find_all()
: name
, attrs
, text
, limit
. 但是只有 find_all()
和 find()
支持recursive
参数.
像调用 find_all()
一样调用tag
find_all()
几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法.BeautifulSoup
对象和 tag
对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all()
方法相同,下面两行代码是等价的:
这两行代码也是等价的:
CSS选择器
Beautiful Soup支持大部分的CSS选择器 http://www.w3.org/TR/CSS2/selector.html [6] , 在 Tag
或 BeautifulSoup
对象的 .select()
方法中传入字符串参数, 即可使用CSS选择器的语法找到tag:
通过tag标签逐层查找:
找到某个tag标签下的直接子标签 [6] :
找到兄弟节点标签:
通过CSS的类名查找:
通过tag的id查找:
同时用多种CSS选择器查询元素:
通过是否存在某个属性来查找:
通过属性的值来查找:
通过语言设置来查找:
返回查找到的元素的第一个
对于熟悉CSS选择器语法的人来说这是个非常方便的方法.Beautiful Soup也支持CSS选择器API, 如果你仅仅需要CSS选择器的功能,那么直接使用 lxml
也可以, 而且速度更快,支持更多的CSS选择器语法,但Beautiful Soup整合了CSS选择器的语法和自身方便使用API.