中文官方文档: https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/
1、使用Beautiful Soup 解析数据
Beautiful Soup是一个用于从HTML和XML文件中提取数据的Python模块,BeautifulSoup自动输入文档转换为Unicode编码,输出文档转换为UTF-编码,开发者不需要考虑编码方式,除非文档没有指定编码方式,这时,Beautiful Soup就不能自动识别编码方式了,需要开发者说明以下原始编码方式就可以了。
1.1 Beautiful Soup的安装
Beautiful Soup 3已经停止开发,目前使用的是Beautiful Soup 4,不过已经移植到了bs4当中,所以导入时,需要from bs4 import Beautiful Soup。如果没用使用Anaconda,则安装Beautiful Soup有以下三种方式:
● 如果使用的是最新版本的Debian或Ubuntu Linux,则可以使用系统软件管理器安装Beautiful Soup。命令:
apt-get install python-bs4
Beautiful Souop 4是通过PyPi发布的,可以通过easy_install或pip来安装。包名是beautifulsoup4,可以兼容Python2和Python3,安装命令:
easy_install beautifulsoup4
或
pip install beautifulsoup4
注意:
在使用Beautiful Soup 4前,需要安装bs4库
pip install bs4
如果当前的Beautiful Soup不是我们想要的版本,可以通过下载源码的方式进行安装,源码的下载地址为:'https://www.crummy.com/software/BeautifulSoup/bs4/download/',然后在控制台中打开源码的指定路径,输入命令python setup.py install 即可。
1.2 解析器
html5lib解析器
用来解析HTML的Python库,按照Web浏览器的方式解析HTML
解析器的比较
解析器 | 用法 | 优点 | 缺点 |
Python标准库 | BeautifulSoup(markup,'html.parser') | Python标准库执行速度适中 | (在Python2.7.3或3.2.2之前的版本中)文档容错能力差 |
lxml的HTML解析器 | BeautifulSoup(markup,'lxml') | 速度快,文档容错能力强 | 需要安装C语言库 |
lxml的XML解析器 | BeautifulSoup(markup,'lxml-xml')BeautifulSoup(markup,'xml') | 速度快唯一支持XML的解析器 | 需要安装C语言库 |
html5lib | BeautifulSoup(markup,'html5lib') | 最好的容错性,以浏览器的方式解析我能当生成HTML格式的文档 | 速度慢,不依赖外部扩展 |
1.3 Beautiful Soup的简单应用
解析文档:
from bs4 import BeautifulSoup
html_doc = open('myBook.html',mode='r',encoding='utf-8')
soup = BeautifulSoup(html_doc,features='lxml')
# print(soup)
print(type(soup)) # <class 'bs4.BeautifulSoup'>
print(soup.prettify()) # 打印格式化的代码 prettify()格式化处理
1.3.1 获取节点
<--myBook.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="css/style.min862f.css?v=4.1.0" rel="stylesheet">
<title>案例</title>
</head>
<body class="fixed-sidebar full-height-layout gray-bg" style="overflow:hidden">
<div id="wrapper">
<p>段落1</p>
<p>段落2</p>
</div>
</body>
</html>
from bs4 import BeautifulSoup
# 创建模拟HTML代码的字符串
html_doc = open('myBook.html',mode='r',encoding='utf-8')
soup = BeautifulSoup(html_doc,'lxml')
print('head节点内容为:\n',soup.head)
print('body节点内容为:\n',soup.body)
print('title节点内容为:\n',soup.title)
print('p节点内容为:\n',soup.p)
说明:
在打印P节点对应的代码时,只打印了第一个P节点,说明当有多个节点时,该选择方式只会获取第一个节点的内容,其他后面的节点讲被忽略。
还可以通过name属性获取节点的名称。
1.3.2 获取节点属性
通过调用attrs即可获取这个节点的所有属性、
from bs4 import BeautifulSoup
html_doc = """
<html>
<head>
<title>横排响应式登录</title>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
<meta name="viewport" content="width=device-width"/>
<link href="font/css/bootstrap.min.css" type="text/css" rel="stylesheet">
<link href="css/style.css" type="text/css" rel="stylesheet">
</head>
<body>
<h3>登录</h3>
<div class="glyphicon glyphicon-envelope"><input type="text" placeholder="请输入邮箱"></div>
<div class="glyphicon glyphicon-lock"><input type="password" placeholder="请输入密码"></div>
</body>
</html>
"""
soup = BeautifulSoup(html_doc,'lxml')
print('meta节点中属性如下:\n',soup.meta.attrs)
print('link节点中属性如下:\n',soup.link.attrs)
print('div节点中属性如下:',soup.div.attrs)
meta节点中属性如下:
{'http-equiv': 'Content-Type', 'content': 'text/html', 'charset': 'utf-8'}
link节点中属性如下:
{'href': 'font/css/bootstrap.min.css', 'type': 'text/css', 'rel': ['stylesheet']}
div节点中属性如下: {'class': ['glyphicon', 'glyphicon-envelope']}
在获取节点中指定属性所对应的值时,除了使用上面的方式外,还可以不写attrs,直接在节点后面已中括号的形式直接添加属性名称来获取对应的值
print('meta节点中http-equif属性值为:',soup.meta['http-equif'])
print('link属性:',soup.link['href'])
print(soup.div['class'][0])
print(soup.div['class'][1])
1.3.3 获取节点包含的文本内容
print('title节点所包含的文本内容为:',soup.title.string)
1.3.4 嵌套获取节点内容
例: 通过"."嵌套获取节点内容
from bs4 import BeautifulSoup
html_doc = open('myBook.html',mode='r',encoding='utf-8')
soup = BeautifulSoup(html_doc,'lxml')
print('head节点内容如下:\n',soup.head)
print('head节点中title节点内容为:',soup.head.title)
print('head节点中的文本内容为:',soup.head.title.string)
1.3.5 关联获取
在获取节点内容时,不一定都能做到一步获取指定节点中的内容,有时还需要先确认某一个节点,然后以该节点为中心获取对应的子节点,孙节点,父节点以及兄弟节点。
-
获取子节点
实现获取某节点下所有子节点内容
在获取某节点下面的所有子节点时,是用那个contents或者childrenn属性来实现,其中contents返回一个列表,在这个列表中的每个元素都是一个子节点内容,而children返回的则是一个“list_iterator”类型的可迭代对象。
from bs4 import BeautifulSoup
html_doc = open('myBook.html',mode='r',encoding='utf-8')
# 创建一个BeautifulSoup对象,获取页面正文
soup = BeautifulSoup(html_doc,features='lxml')
print("soup.head.contents:",soup.head.contents) # 返回是一个列表
print("soup.head.children:",soup.head.children) # 返回是一个list_iterator迭代对象
for i in soup.head.children:
print("i:",i)
运行结果:
soup.head.contents: ['\n', <meta charset="utf-8"/>, '\n', <link href="css/style.min862f.css?v=4.1.0" rel="stylesheet"/>, '\n', <title>案例</title>, '\n']
soup.head.children: <list_iterator object at 0x000002233EEFD520>
i:
i: <meta charset="utf-8"/>
i:
i: <link href="css/style.min862f.css?v=4.1.0" rel="stylesheet"/>
i:
i: <title>案例</title>
i:
-
获取孙节点
使用descendants属性获取子孙节点内容
该属性返回一个generator对象,获取该对象中的所有内容,同样可以直接将其转换为list类型或者通过for循环遍历的方式进行获取。
<--myBook.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="css/style.min862f.css?v=4.1.0" rel="stylesheet">
<title>案例</title>
</head>
<body class="fixed-sidebar full-height-layout gray-bg" style="overflow:hidden">
<div id="wrapper">
<p>段落1</p>
<p>段落2</p>
</div>
</body>
</html>
代码:
from bs4 import BeautifulSoup
html_str = open('myBook.html',mode='r',encoding='utf-8')
soup = BeautifulSoup(html_str,features='lxml')
descendants = soup.body.descendants
for i in descendants:
print("i:",i)
运行结果:
i:
i: <div id="wrapper">
<p>段落1</p>
<p>段落2</p>
</div>
i:
i: <p>段落1</p>
i: 段落1
i:
i: <p>段落2</p>
i: 段落2
i:
i:
-
获取父节点
获取父节点有两种方式:一种是通过parent属性直接获取指定节点的父节点内容,还可以通过parents属性获取指定节点的父节点及以上(祖先节点)内容,只是parents属性会返回一个generator对象,获取该对象的所有内容时,同样可以直接将其转换为list类型或者通过for循环的方式进行获取
from bs4 import BeautifulSoup
html_str = open('myBook.html',mode='r',encoding='utf-8')
soup = BeautifulSoup(html_str,features='lxml')
print('title.parent:',soup.title.parent)
print('title.parents:',soup.title.parents)
for i in soup.title.parents:
print('i.name:',i.name)
运行结果:
title.parent:
<head>
<meta charset="utf-8"/>
<link href="css/style.min862f.css?v=4.1.0" rel="stylesheet"/>
<title>案例</title>
</head>
title.parents: <generator object PageElement.parents at 0x0000019147DF6580>
i.name: head
i.name: html
i.name: [document]
-
获取兄弟节点
<--myBook.html-->
<html>
<head>
<title>关联获取演示</title>
<meta charset="utf-8"/>
</head>
<body>
<p class="p-1" value="1">
<a href="https://item.jd.com/12353915.html">零基础学Python</a>
</p>
第一个p节点下文本
<div class="div-1" value="2">
<a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a>
</div>
<p class="p-3" value="3">
<a href="https://item.jd.com/12512461.html">Python项目开发案例集锦</a>
</p>
<div class="div-2" value="4">
<a href="https://item.jd.com/12550531.html">Python编程锦囊</a>
</div>
</body>
</html>
from bs4 import BeautifulSoup
html_doc = open('myBook.html',mode='r',encoding='utf-8')
soup = BeautifulSoup(html_doc,features='lxml')
print('p.next_sibling:',soup.div.next_sibling)
div = soup.p.next_sibling.next_sibling
print("div:",div)
运行结果:
p.next_sibling:
第一个p节点下文本
div: <div class="div-1" value="2">
<a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a>
</div>
说明:
如果标签与标签之间有换行,那么下一个节点获取到的就是空。比如获取第一个div节点的下一个节点获取到的就是空,下下一个节点才是p节点
1.4 使用find()方法获取
1.4.1 find_all()
from bs4 import BeautifulSoup
html_doc = """
<html>
<head>
<title>关联获取演示</title>
<meta charset="utf-8"/>
</head>
<body>
<p class="p-1" value = "1"><a href="https://item.jd.com/12353915.html">零基础学Python</a></p>
<p class="p-2" value = "2"><a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a></p>
<p class="p-3" value = "3"><a href="https://item.jd.com/12512461.html">Python项目开发案例集锦</a></p>
<div class="div-2" value = "4"><a href="https://item.jd.com/12550531.html">Python编程锦囊</a></div>
</body>
</html>
"""
soup = BeautifulSoup(html_doc,features='lxml')
print('所有p标签:',soup.find_all(name='p'))
print('第一个p标签的a标签:',soup.find_all(name='p')[0].find_all('a')[0])
运行结果:
所有p标签: [<p class="p-1" value="1"><a href="https://item.jd.com/12353915.html">零基础学Python</a></p>, <p class="p-2" value="2"><a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a></p>, <p class="p-3" value="3"><a href="https://item.jd.com/12512461.html">Python项目开发案例集锦</a></p>]
第一个p标签的a标签: <a href="https://item.jd.com/12353915.html">零基础学Python</a>
1.4.2 attrs参数
from bs4 import BeautifulSoup
html_doc = """
<html>
<head>
<title>关联获取演示</title>
<meta charset="utf-8"/>
</head>
<body>
<p class="p-1" value = "1"><a href="#">1</a></p>
<p class="p-1" value = "2"><a href="#">2</a></p>
<p class="p-3" value = "3"><a href="#">3</a></p>
<div class="div-2" value = "4"><a href="#">4</a></div>
</body>
</html>
"""
soup = BeautifulSoup(html_doc,features='lxml')
print('value=1的p标签:',soup.find_all('p',attrs={"value":1}))
print('value=1的所有标签:',soup.find_all(attrs={'value':1}))
print('打印class为p-1的所有内容:',soup.find_all(class_='p-1'))
print('打印value=3的内容:',soup.find_all(value='3'))
运行结果:
value=1的p标签: [<p class="p-1" value="1"><a href="#">1</a></p>]
value=1的所有标签: [<p class="p-1" value="1"><a href="#">1</a></p>]
打印class为p-1的所有内容: [<p class="p-1" value="1"><a href="#">1</a></p>, <p class="p-1" value="2"><a href="#">2</a></p>]
打印value=3的内容: [<p class="p-3" value="3"><a href="#">3</a></p>]
1.4.3 text参数
from bs4 import BeautifulSoup
import re
html_doc = """
<html>
<head>
<title>关联获取演示</title>
<meta charset="utf-8"/>
</head>
<body>
<p class="p-1" value = "1"><a href="#">Java</a></p>
<p class="p-1" value = "2"><a href="#">Python入门</a></p>
<p class="p-3" value = "3"><a href="#">C++</a></p>
<div class="div-2" value = "4"><a href="#">Vb</a></div>
</body>
</html>
"""
soup = BeautifulSoup(html_doc,features='lxml')
print('指定字符串锁获取的内容如下:',soup.find_all(text='Java'))
print('指定正则表达式对象锁获取的内容如下:',soup.find_all(text=re.compile('Python')))
运行结果:
指定字符串锁获取的内容如下: ['Java']
指定正则表达式对象锁获取的内容如下: ['Python入门']
1.4.4 find() 匹配第一个节点内容
from bs4 import BeautifulSoup
import re
html_doc = """
<html>
<head>
<title>关联获取演示</title>
<meta charset="utf-8"/>
</head>
<body>
<p class="p-1" value = "1" id="p1"><a href="#">零基础学Python</a></p>
<p class="p-2" value = "2" id="p2"><a href="#">Python从入门到项目实践</a></p>
<p class="p-3" value = "3" id="p3"><a href="#">Python项目开发案例集锦</a></p>
<p class="p-3" value = "4" id="p4"><a href="#">Python项目开发案例集锦</a></p>
<div class="div-2" value = "4"><a href="#">Python编程锦囊</a></div>
</body>
</html>
"""
soup = BeautifulSoup(html_doc,features='lxml')
print(soup.find(name='p'))
print(soup.find(class_='p-3'))
print(soup.find(attrs={'value':'4'}))
print(soup.find(text=re.compile('Python')))
print(soup.find('p',class_='p-3'))
print(soup.find('p',class_='p-3',value='3'))
print(soup.find('p',class_='p-3',id='p4'))
运行结果:
<p class="p-1" id="p1" value="1"><a href="#">零基础学Python</a></p>
<p class="p-3" id="p3" value="3"><a href="#">Python项目开发案例集锦</a></p>
<p class="p-3" id="p4" value="4"><a href="#">Python项目开发案例集锦</a></p>
零基础学Python
<p class="p-3" id="p3" value="3"><a href="#">Python项目开发案例集锦</a></p>
<p class="p-3" id="p3" value="3"><a href="#">Python项目开发案例集锦</a></p>
<p class="p-3" id="p4" value="4"><a href="#">Python项目开发案例集锦</a></p>
1.4.5 其他方法
根据条件获取节点内容的其他方法
方法名称 | 描述 |
find_parent() | 获取父节点内容 |
find_parents() | 获取所有祖先节点内容 |
find_next_sibling() | 获取后面第一个兄弟节点内容 |
find_next_siblings() | 获取后面所有兄弟节点内容 |
find_previous_sibling() | 获取前面第一个兄弟节点内容 |
find_previous_siblings() | 获取前面所有兄弟节点内容 |
find_next() | 获取当前节点的下一个第一个符合条件的节点内容 |
find_all_next() | 获取当前节点的下一个所有符合条件的节点内容 |
find_previous() | 获取第一个符合条件的内容 |
find_all_previous() | 获取所有符合条件的节点内容 |
1.4.6 CSS选择器
Beautiful Soup模块还提供了CSS选择器来获取节点内容,如果Tag或者是BeautifulSoup对象都可以直接调用select()方法,然后填写指定参数即可通过CSS选择器获取到节点的内容。
在使用CSS选择器获取节点内容时,首先需要调用select()方法,然后为其指定字符串类型的CSS选择器。
常见的CSS选择器如下:
- 直接填写字符串类型的节点名称
- .class :表示指定class属性值
- #id:表示指定id属性的值
from bs4 import BeautifulSoup # 导入BeautifulSoup库
html_doc = """
<html>
<head>
<title>关联获取演示</title>
<meta charset="utf-8"/>
</head>
<body>
<div class="test_1" id="class_1">
<p class="p-1" value = "1"><a href="https://item.jd.com/12353915.html">零基础学Python</a></p>
<p class="p-2" value = "2"><a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a></p>
<p class="p-3" value = "3"><a href="https://item.jd.com/12512461.html">Python项目开发案例集锦</a></p>
<p class="p-4" value = "4"><a href="https://item.jd.com/12550531.html">Python编程锦囊</a></p>
</div>
<div class="test_2" id="class_2">
<p class="p-5"><a href="https://item.jd.com/12185501.html">零基础学Java(全彩版)</a></p>
<p class="p-6"><a href="https://item.jd.com/12199033.html">零基础学Android(全彩版)</a></p>
<p class="p-7"><a href="https://item.jd.com/12250414.html">零基础学C语言(全彩版)</a></p>
</div>
</body>
</html>
"""
soup = BeautifulSoup(html_doc,features='lxml')
print("所有p节点内容:",soup.select('p'))
print('所有p节点中的第二个p节点内容:',soup.select('p')[1])
print('类型为:test_2锁对应的节点',soup.select('.test_2'))
print('id值为class_1锁对应的节点:',soup.select('#class_1'))
运行结果:
1、所有p节点内容: [<p class="p-1" value="1"><a href="https://item.jd.com/12353915.html">零基础学Python</a></p>, <p class="p-2" value="2"><a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a></p>, <p class="p-3" value="3"><a href="https://item.jd.com/12512461.html">Python项目开发案例集锦</a></p>, <p class="p-4" value="4"><a href="https://item.jd.com/12550531.html">Python编程锦囊</a></p>, <p class="p-5"><a href="https://item.jd.com/12185501.html">零基础学Java(全彩版)</a></p>, <p class="p-6"><a href="https://item.jd.com/12199033.html">零基础学Android(全彩版)</a></p>, <p class="p-7"><a href="https://item.jd.com/12250414.html">零基础学C语言(全彩版)</a></p>]
2、所有p节点中的第二个p节点内容: <p class="p-2" value="2"><a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a></p>
3、类型为:test_2锁对应的节点 [<div class="test_2" id="class_2">
<p class="p-5"><a href="https://item.jd.com/12185501.html">零基础学Java(全彩版)</a></p>
<p class="p-6"><a href="https://item.jd.com/12199033.html">零基础学Android(全彩版)</a></p>
<p class="p-7"><a href="https://item.jd.com/12250414.html">零基础学C语言(全彩版)</a></p>
</div>]
4、id值为class_1锁对应的节点: [<div class="test_1" id="class_1">
<p class="p-1" value="1"><a href="https://item.jd.com/12353915.html">零基础学Python</a></p>
<p class="p-2" value="2"><a href="https://item.jd.com/12451724.html">Python从入门到项目实践</a></p>
<p class="p-3" value="3"><a href="https://item.jd.com/12512461.html">Python项目开发案例集锦</a></p>
<p class="p-4" value="4"><a href="https://item.jd.com/12550531.html">Python编程锦囊</a></p>
</div>]
根据条件获取节点内容的其他方法
获取节点内容方式 | 描述 |
soup.select('div[class="test_1"]')[0].select('p')[0] | 嵌套获取class名为test_1对应的div中的所有p节点中的第一个 |
soup.select('p')[0]['value'] | 获取所有p节点中第一个节点内value属性对应的值(两种方法) |
soup.select('p')[0].attrs['value'] | |
soup.select('p')[0].get_text() | 获取所以p节点中第一个节点内的文本 |
soup.select('p')[0].string | |
soup.select('p')[1:] | 获取所有p节点中第二个以后的p节点 |
soup.select('.p-1,.p-5') | 获取class名为p-1与p-5对应的节点 |
soup.select('a[href]') | 获取存在href属性的所有a节点 |
soup.select('p[value="1"]') | 获取所有属性值为value='1'的p节点 |