06 解析库Beautiful Soup
一、Beautiful Soup介绍
Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3 目前已经停止开发,官网推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4
#安装 Beautiful Soup
pip install beautifulsoup4
#安装解析器
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:
$ apt-get install Python-lxml
$ easy_install lxml
$ pip install lxml
另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:
$ apt-get install Python-html5lib
$ easy_install html5lib
$ pip install html5lib
下表列出了主要的解析器,以及它们的优缺点,官网推荐使用lxml作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
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格式的文档 | 速度慢不依赖外部扩展 |
中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
二、基本使用
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 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>
"""
#基本使用:容错处理,文档的容错能力指的是在html代码不完整的情况下,使用该模块可以识别该错误。使用BeautifulSoup解析上述代码,能够得到一个 BeautifulSoup 的对象,并能按照标准的缩进格式的结构输出
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml') #具有容错功能
res=soup.prettify() #处理好缩进,结构化显示
print(res)
三、遍历文档树
#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
1、用法
2、获取标签的名称
3、获取标签的属性
4、获取标签的内容
5、嵌套选择
6、子节点、子孙节点
7、父节点、祖先节点
8、兄弟节点
# 示例
from bs4 import BeautifulSoup
'''基本参数
markup='' 传入解析文本
BeautifulSoup('解析文本内容','解析器')
html.parser :python自带的写解析
lxml:pip3 install lxml 第三方的解析库
'''
'''基础使用'''
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 sisters; and their names were
<a id="tank" href="http://example.com/tank">今天是个好日子,祝大家越来越美丽</a>
<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>
"""
soup = BeautifulSoup(html_doc,'html.parser')
# print(soup) # 就是html内容,会自动将标签补全
# print(type(soup))
# print(soup.prettify()) # 将html文本美化 了解
'''基本用法
遍历文档树,即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
'''
'''1.直接.标签名'''
print('1.直接.标签名'+'*'*100)
print(soup.head)
print(soup.a)
'''2.获取标签的名称 了解'''
print('2.获取标签的名称 了解'+'*'*100)
print(soup.p.name)
'''3、获取标签的属性 (*********)'''
print('3、获取标签的属性'+'*'*100)
print(soup.a.attrs) # 只返回第一个a标签的所有属性
print(type(soup.a.attrs)) # 返回的是一个字典
print(soup.a.attrs.get('id')) # 获取a标签中id属性值
print(soup.a.attrs.get('href')) # 获取a标签中href属性值
'''4、获取标签的文本内容'''
print('4、获取标签的文本内容'+'*'*100)
print(soup.a.text)# 获取第一个a标签中的文本内容
'''5.嵌套选择'''
print('5.嵌套选择'+'*'*100)
print(type(soup.html)) # 就是Beaytifulsoup类的对象
print(type(soup.html.head.title))
print(soup.html.head.title) # 选择soup.html中head下面的title
'''6、子节点、子孙节点'''
print('6、子节点、子孙节点'+'*'*100)
print(soup) #注意: 换行符也算一个 节点
print(soup.body.contents) # body下所有子节点,返回的是列表
print(len(soup.body.contents)) # body下所有子节点
'''优先使用children'''
print('优先使用children'+'*'*100)
print(soup.body.children) # 得到一个迭代器,包含p下所有子节点
print(list(soup.body.children)) # 得到一个迭代器,包含p下所有子节点
'''7、父节点、祖先节点'''
print('7、父节点、祖先节点'+'*'*100)
print(soup.a.parent) # 仅仅获取a标签的父节点
print(soup.a.parents) # 找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...,返回的是一个生成器
print(list(soup.a.parents)) # 找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...
'''8、兄弟节点'''
print('8、兄弟节点'+'*'*100)
print(soup.a.next_sibling) # 下一个兄弟,'\n'也算一个标签,也算是a标签的一个兄弟
print(soup.a.previous_sibling) # 上一个兄弟,a标签的父级中有文本属性,所以也算是一级''
print(soup.a.next_siblings) # 下面的兄弟们=>生成器对象
print(list(soup.a.next_siblings))
print(soup.a.previous_siblings) # 上面的兄弟们=>生成器对象
print(list(soup.a.previous_siblings))
四、搜索文档树
1. 五种过滤器
标签查找与属性查找:
- 根据标签查找属性:
查找第一个标签soup.find(name 属性匹配attrs 属性查找匹配text 文本匹配)
- 查找第一个a标签soup.find(name='a',attrs={'class': '属性值'})
查找所有标签
- soup.find_all()
标签:
- 字符串过滤器 字符串全局匹配
- name = 'p'
name 属性匹配
attrs 属性查找匹配
text 文本匹配
- 正则过滤器 re模块匹配
- name = re.compile()
name 属性匹配
attrs 属性查找匹配
text 文本匹配
- 列表过滤器 列表内的数据匹配
- name = ['tank', 100]
name 属性匹配
attrs 属性查找匹配
text 文本匹配
- bool过滤器 rue匹配
- name = True
name 属性匹配
attrs 属性查找匹配
text 文本匹配
- 方法过滤器 于一些要的属性以及不需要的属性查找。
- name = func()
name 属性匹配
attrs 属性查找匹配
text 文本匹配
属性:
- class_
- id
1.字符串:即标签名
from bs4 import BeautifulSoup
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 sisters; and their names were
<a id="tank" href="http://example.com/tank">今天是个好日子,祝大家越来越美丽</a>
<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>
"""
soup = BeautifulSoup(html_doc, 'lxml')
'''1.字符串过滤器'''
print('*'*100,'(1).根据name查找节点标签')
a_tag = soup.find(name='a') # 只查第一个
print(a_tag)
print('*'*100,'(2).根据attrs属性查找节点标签')
a_tag = soup.find(attrs={"id": "tank"})
print(a_tag)
print('*'*100,'(3).根据文本找节点')
a_tag = soup.find(text='啊') # 根据文本查找的时候,文本必须要一摸一样才可以匹配成功
print(a_tag)
print('*'*100,'(4).name与attrs配合使用')
# attrs是以字典的形式用
res = soup.find(name='a', attrs={'id': 'link3'}, text='Tillie')
print(res)
2. re正则过滤器
'''2.正则过滤器
re模块匹配
- name = re.compile()
name 属性匹配
attrs 属性查找匹配
text 文本匹配
'''
import re
from bs4 import BeautifulSoup
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 sisters; and their names were
<a id="tank" href="http://example.com/tank">今天是个好日子,祝大家越来越美丽</a>
<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>
"""
soup = BeautifulSoup(html_doc, 'lxml')
print('*'*100,'2.正则过滤器--查找标签名字中包含a的节点')
res = soup.find(name=re.compile('a'))# 查找标签名字中包含a的节点
print(res)
3. 列表过滤器
'''3.列表过滤器
列表内的数据匹配
- name = ['tank', 100]
name 属性匹配
attrs 属性查找匹配
text 文本匹配
'''
from bs4 import BeautifulSoup
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 sisters; and their names were
<a id="tank" href="http://example.com/tank">今天是个好日子,祝大家越来越美丽</a>
<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>
"""
soup = BeautifulSoup(html_doc, 'lxml')
print('*'*100,'3.列表过滤器--获取第一个a标签 或 者p标签的节点')
res = soup.find(name=['a', 'p'])# 获取第一个a标签 或 者p标签的节点
print(res)
print('*'*100,'3.列表过滤器--获取所有的a标签与p标签的节点')
res = soup.find_all(name=['a', 'p'])# 获取所有的a标签与p标签的节点
print(res)
4.布尔过滤器
'''4.bool过滤器 (了解)
True匹配
name 属性匹配
attrs 属性查找匹配
text 文本匹配
'''
from bs4 import BeautifulSoup
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 sisters; and their names were
<a id="tank" href="http://example.com/tank">今天是个好日子,祝大家越来越美丽</a>
<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>
"""
soup = BeautifulSoup(html_doc, 'lxml')
print('*'*100,'4.bool过滤器 (了解)--获取第一个a标签 或 者p标签的节点')
print(soup.find_all(True))
for tag in soup.find_all(True):
print(tag.name)
5.方法过滤器
'''5.方法过滤器
用于一些要的属性以及不需要的属性查找。
- name = func()
name 属性匹配
attrs 属性查找匹配
text 文本匹配
'''
from bs4 import BeautifulSoup
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 sisters; and their names were
<a id="tank" href="http://example.com/tank">今天是个好日子,祝大家越来越美丽</a>
<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>
"""
soup = BeautifulSoup(html_doc, 'lxml')
print('*'*100,'5.方法过滤器--判断节点中有class没有id的节点,并将该节点返回')
# 函数用于判断节点中有class没有id的节点,并将该节点返回
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
print(soup.find_all(has_class_but_no_id))
2. CSS选择器
from bs4 import BeautifulSoup
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 sisters; and their names were
<a id="tank" href="http://example.com/tank">今天是个好日子,祝大家越来越美丽</a>
<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>
"""
soup = BeautifulSoup(html_doc, 'lxml')
# CSS选择器
res = soup.select('#tank') # 选择id属性为tank
# print(res)
res1 = soup.select('.story')# 选择class属性为story包括他的儿子,返回列表
# print(res1)
res2 = soup.select('.story')[0].select('#tank') # 选择class属性为story下的id为tank的
print(res2)
# 可以用多个select,但其实一个select就可以解决
res3 = soup.select('.story #tank') # 选择class属性为story下的id为tank的
print(res3)
# 获取属性
res4 = soup.select('.story #tank')[0].attrs # 返回字典
print(res4)
# 获取文本
res5 = soup.select('.story #tank')[0].get_text()
print(res5)
五、修改文档树
https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#id40
六、总结
# 总结:
#1、推荐使用lxml解析库
#2、讲了三种选择器:标签选择器,find与find_all,css选择器
1、标签选择器筛选功能弱,但是速度快
2、建议使用find,find_all查询匹配单个结果或者多个结果
3、如果对css选择器非常熟悉建议使用select
#3、记住常用的获取属性attrs和文本值get_text()的方法