一、 Beautiful Soup 安装
Beautiful Soup 提供一些简单的、Python 式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。
Beautiful Soup 3 目前已经停止开发,推荐在现在的项目中使用 Beautiful Soup 4,不过它已经被移植到 BS4 了,也就是说导入时我们需要导入 bs4。
# 安装 Beautiful Soup 4
pip install beautifulsoup4
Beautiful Soup 支持 Python 标准库中的 HTML 解析器,还支持一些第三方的解析器,如果我们不安装则 Python 会使用默认的解析器,但 lxml 解析器更加强大,速度更快,推荐安装。
pip install lxml
以下是一些解析器的信息:
解析器 | 使用方法 | 优势 | 劣势 |
Python标准库 | BeautifulSoup(markup, “html.parser”) | Python的内置标准库 执行速度适中 文档容错能力强 | Python 2.7.3 or 3.2.2 前 的版本中,文档容错能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, “lxml”) | 速度快 文档容错能力强 | 需要安装C语言库 |
lxml XML 解析器 | BeautifulSoup(markup, [“lxml”, “xml”]) BeautifulSoup(markup, “xml”) | 速度快 唯一支持XML的解析器 | 需要安装C语言库 |
html5lib | BeautifulSoup(markup, “html5lib”) | 最好的容错性 以浏览器的方式解析文档 生成HTML5格式的文档 | 速度慢 |
二、 创建 Beautiful Soup 对象
from bs4 import BeautifulSoup
html_doc = """
<html>
<head>
<title>The Dormouse's story</title>
</head>
<body>
<p class="title aq">
<b>
The Dormouse's story
</b>
</p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</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>
"""
# BeautifulSoup 会自动检测传入文件的编码格式然后转化为 Unicode 格式
soup = BeautifulSoup(html_doc, 'lxml')
# 输出第一个 title 标签
soup.title
# 输出第一个 title 标签的标签名称
soup.title.name
# 输出第一个 title 标签的包含内容
soup.title.string
# 输出第一个 title 标签的父标签的标签名称
soup.title.parent.name
# 输出第一个 p 标签
soup.p
# 输出第一个 p 标签的 class 属性内容
soup.p['class']
# 输出第一个 a 标签的 href 属性内容
soup.a['href']
# soup 的属性可以被添加、删除或修改,其属性操作方法与字典一样
# 修改第一个 a 标签的 href 属性为 http://www.baidu.com/
soup.a['href'] = 'http://www.baidu.com/'
# 给第一个 a 标签添加 name 属性
soup.a['name'] = u'百度'
# 删除第一个 a 标签的 class 属性
del soup.a['class']
三、 四大对象种类
Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python 对象,可以归纳为4种。
1. Tag
Tag 通俗点讲就是 HTML 中的一个个标签,利用 soup 加标签名的方式可以轻松地获取这些标签的内容,不过有一点是它查找的是在所有内容中的第一个符合要求的标签。
对于 Tag,它有两个重要的属性:name 和 attrs。
# soup 对象本身比较特殊,它的 name 即为标签本身的名称
soup.title.name
# 通过 Tag.attrs 返回字典结构的属性
soup.p.attrs
2. NavigableString
要想获取标签内部的文字用 .string 即可。
# 返回类型是一个 NavigableString
soup.title.string
3. BeautifulSoup
BeautifulSoup 对象表示的是一个文档的全部内容,大部分时候可以把它当作一个特殊 Tag 对象,可以分别获取它的类型,名称,以及属性。
soup.name
soup.attrs
4. Comment
Comment 对象是一个特殊类型的 NavigableString 对象,其实输出的内容仍然不包括注释符号,但是如果不好好处理它,可能会对我们的文本处理造成意想不到的麻烦。
# Tag 结果
<a class="sister" href="<http://example.com/elsie>" id="link1"><!-- Elsie --></a>
# soup.a.string 结果
Elsie
# 返回类型
<class 'bs4.element.Comment'>
a 标签里的内容实际上是注释,但是如果我们利用 .string 来输出它的内容会发现它已经把注释符号去掉了,所以这可能会给我们带来不必要的麻烦。
# 首先判断了类型是否为 Comment 类型然后再进行其他操作
if type(soup.a.string)==bs4.element.Comment:
print soup.a.string
四、遍历文档树
1. 直接子节点
# Tag.contents: 以列表形式返回所有子节点
soup.head.contents
# 使用 [num] 的形式获得具体值
soup.head.contents[1]
# Tag.children: 生成器可用于循环访问
soup.head.children
# 遍历一下
for child in soup.body.children:
print(child)
2. 所有子孙节点
# Tag.descendants: 对所有 tag 的子孙节点进行递归循环,和 children类似需要遍历获取内容
for child in soup.descendants:
print(child)
3. 节点内容
# Tag.String: Tag 只有一个 String 子节点是可以这么访问,否则返回 None
soup.head.string
# Tag.Strings: 生成器可用于循环访问
4. 多个内容
# Tag.strings: 获取多个内容不过需要遍历获取
for string in soup.strings:
print(repr(string))
# Tag.stripped_strings: 获取多个内容不过需要遍历获取(去除多余空白内容)
for string in soup.stripped_strings:
print(repr(string))
5. 父节点
# Tag.parent: 父节点
body = soup.title
body.parent
# Tag.parents: 父到根的所有节点
body.parents
6. 全部父节点
# Tag.parents: 父到根的所有节点
content = soup.head.title.string
for parent in content.parents:
print(parent.name)
7. 兄弟节点
# Tag.next_sibling: 兄弟节点可以理解为和本节点处在统一级的节点(空白或者换行也可以被视作一个节点)
# Tag.previous_sibling
soup.p.next_sibling
8. 全部兄弟节点
# Tag.next_siblings: 对当前节点的兄弟节点迭代输出
# Tag.previous_siblings
for sibling in soup.a.next_siblings:
print(repr(sibling))
9. 前后节点
# Tag.next_element
# Tag.previous_element
# 与 .next_sibling .previous_sibling 不同,它并不是针对于兄弟节点而是在所有节点,不分层次
soup.head.next_element
10. 所有前后节点
# .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
for element in soup.head.next_elements:
print(repr(element))
五、搜索文档树
find_all() 方法搜索当前 tag 的所有 tag 子节点并判断是否符合过滤器的条件。
1. name 参数
# name 参数可以查找所有名字为 name 的 tag,字符串对象会被自动忽略掉
# 第一个参数为Tag的名称,第二个参数为匹配的属性
# 字符串参数
soup.find_all('p', 'story')
# 正则表达式
# import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# 列表:将与列表中任一元素匹配的内容返回
soup.find_all(['a', 'b'])
# True:可以匹配任何值,查找到所有的tag,但是不会返回字符串节点
for tag in soup.find_all(True):
print(tag.name)
# 函数:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数
2. keyword 参数
# 如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索
# 如果包含一个名字为 id 的参数,Beautiful Soup 会搜索每个 tag 的 id 属性
soup.find_all(id='link2')
# 如果传入 href 参数,Beautiful Soup 会搜索每个 ta g的 href 属性
soup.find_all(href=re.compile("elsie"))
# 使用多个指定名字的参数可以同时过滤 tag 的多个属性
soup.find_all(href=re.compile("elsie"), id='link1')
# 想用 class 过滤,不过 class 是 python 的关键词,加个下划线就可以
soup.find_all("a", class_="sister")
# attrs 参数定义一个字典参数来搜索包含特殊属性的 tag
soup.find_all(attrs={"data-foo": "value"})
3. text 参数
# text 参数可以搜搜文档中的字符串内容,与 name 参数的可选值一样, text 参数接受字符串、正则表达式、列表、True
soup.find_all(text="Elsie")
soup.find_all(text=["Tillie", "Elsie", "Lacie"])
soup.find_all(text=re.compile("Dormouse"))
4. limit 参数
# find_all()方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.
# 如果我们不需要全部结果可以使用 limit 参数限制返回结果的数量,效果与 SQL 中的 limit 关键字类似,当搜索到的结果数量达到 limit 的限制时就停止搜索返回结果
soup.find_all("a", limit=2)
5. recursive 参数
# 调用 tag 的 find_all() 方法时,Beautiful Soup 会检索当前 tag 的所有子孙节点,如果只想搜索 tag 的直接子节点可以使用参数 recursive=False
soup.html.find_all("title", recursive=False)
find() 与 find_all() 方法唯一的区别是 find_all() 方法的返回结果是包含一个元素的列表,而 find() 方法直接返回结果。
# tag搜索
# 直接搜索名为tagname的tag 如:find('head')
find(tagname)
# 搜索在list中的tag,如: find(['head', 'body'])
find(list)
# 搜索在dict中的tag,如:find({'head':True, 'body':True})
find(dict)
# 搜索符合正则的tag, 如:find(re.compile('^p')) 搜索以p开头的tag
find(re.compile(''))
# 搜索函数返回结果为true的tag, 如:find(lambda name: if len(name) == 1) 搜索长度为1的tag
find(lambda)
# 搜索所有tag
find(True)
# attrs搜索
# 寻找id属性为xxx的
find(id='xxx')
# 寻找id属性符合正则且algin属性为xxx的
find(attrs={id=re.compile('xxx'), algin='xxx'})
# 寻找有id属性但是没有algin属性的
find(attrs={id=True, algin=None})
六、CSS选择器
1. 通过标签名查找
soup.select('title')
2. 通过类名查找
soup.select('.sister')
3. 通过 id 名查找
soup.select('#link1')
4. 组合查找
# 查找 p 标签中,id 等于 link1 的内容,二者需要用空格分开
# 空格分开
soup.select('p #link1')
# 子标签查找
soup.select('head > title')
5. 属性查找
soup.select('a[class="sister"]')
# 组合方式查找,不在同一节点的空格隔开,同一节点的不加空格
soup.select('p a[href="<http://example.com/elsie>"]')
# 返回的结果都是列表形式,可以遍历形式输出然后用 get_text() 方法来获取它的内容
soup.select('title')[0].get_text()
for title in soup.select('title'):
print(title.get_text())