本文主要是介绍网页数据的解析提取(Beautiful Soup库详解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前面介绍了lxml库和re库,(网页数据的解析提取(XPath的使用----lxml库详解)-CSDN博客 网页数据的解析提取(正则表达式----re库详解)-CSDN博客)。现在来介绍一个更为简便和强大的解析工具--Beautiful Soup,其借助网页的结构和属性等特性来解析网页。
目录
Beautiful Soup
1、 Beautiful Soup 的简介
2、解析器
3、准备工作
4、基本使用
5、提取信息
(1)获取名称
(2)获取属性
(3)获取内容
(4)嵌套选择
(5)关联选择
(6)方法选择器
6、CSS选择器
7、总结
Beautiful Soup
1、 Beautiful Soup 的简介
简单来说, Beautiful Soup 是 Python的一个 HTML 或XML 的解析库, 我们用它可以方便地从网页中提取数据, 其官方解释如下:
Beautiful Soup 提供一些简单的、Python 式的函数来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据, 因为简单, 所以无须很多代码就可以写出一个完整的应用程序。Beautiful Soup 自动将输入文档转换为Unicode 编码, 将输出文档转换为 utf-8编码。你不需要考虑编码方式, 除非文档没有指定具体的编码方式,这时你仅仅需要说明一下原始编码方式就可以了。Beautiful Soup 已成为和 lxml、html5lib一样出色的 Python 解释器, 为用户灵活提供不同的解析策略或强劲的速度。总而言之, 利用Beautiful Soup 可以省去很多烦琐的提取工作, 提高解析网页的效率。
2、解析器
实际上, Beautiful Soup在解析时是依赖解析器的,它除了支持 Python标准库中的 HTML 解析器,还支持一些第三方解析器(例如lxml)。下表列出了 Beautiful Soup 支持的解析器。
解 析 器 | 使 用 方 法 | 优 势 | 劣 势 | |
Python标准库 | BeautifulSoup(markup,'html.parser') | Python 的内置标准库、执行速度适中、文档容错能力强 | Python 2.7.3 或 3.2.2 前的版本中文容错能力差 | |
LXML HTML解析器 | BeautifulSoup(markup, 'lxml') | 速度快、文档容错能力强 | 需要安装C语言库 | |
LXML XML 解析器 | BeautifulSoup(markup, 'xml') | 速度快、唯一支持 XML 的解析器 | 需要安装C语言库 | |
html5lib | BeautifulSoup(markup, 'htm15lib') | 提供最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档 | 速度慢、不依赖外部扩展 |
通过上表的对比可以看出,LXML解析器有解析HTML 和XML的功能,而且速度快、容错能力强,所以推荐使用它。使用LXML 解析器, 只需在初始化 Beautiful Soup时, 把第二个参数改为 lxml即可:
from bs4 import BeautifulSoupsoup = BeautifulSoup('', 'lxml')print(soup. p. string)
在后面, 统一用这个解析器演示 Beautiful Soup 的用法实例。
3、准备工作
在开始之前, 请确保已经正确安装好 Beautiful Soup 和 lxml这两个库。Beautiful Soup 直接使用pip3 安装即可, 命令如下:
pip3 install beautifulsoup4
更加详细的安装说明可以参考:https://setup.scrape.center/beautifulsoup 。另外,我们使用的是 lxml这个解析器,所以还需要额外安装lxml这个库。以上两个库都安装完成后,就可以进行接下来的学习了。
4、基本使用
下面首先通过实例看看 Beautiful Soup的基本用法:
html= """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.prettify())
print(soup.title.string)
结果如下:
<html><head><title>The Dormouse's story</title></head><body><p class="title" name="dromouse"><b>The Dormouse's story</b></p><p class="story">Once upon a time there were three little sisters; and their names were<a class="sister" href=" http://example.com/elsie " id="link1"><!--Elsie--></a>,<a class="sister" href=" http://example.com/lacie " id="link2">Lacie</a>and<a class="sister" href=" http://example.com/tillie " id="link3">Tillie</a>;
and they lived at the bottom of a well.</p><p class="story">…</p></body>
</html>The Dormouse's story
这里首先声明一个变量 html,这是一个 HTML 字符串。但是需要注意的是,它并不是一个完整的 HTML 字符串,因为 body 节点和 html节点都没有闭合。接着,我们将它当作第一个参数传给BeautifulSoup 对象,该对象的第二个参数为解析器的类型(这里使用 1xml),此时就完成了BeaufulSoup对象的初始化。然后,将这个对象赋值给 soup 变量。之后就可以调用 soup的各个方法和属性解析这串HTML 代码了。
首先,调用prettify方法。这个方法可以把要解析的字符串以标准的缩进格式输出。这里需要注意的是, 输出结果里包含 body 和 html节点, 也就是说对于不标准的 HTML 字符串 BeautifulSoup,可以自动更正格式。这一步不是由 prettify方法完成的,而是在初始化BeautifulSoup的时候就完成了。然后调用 soup. title. string, 这实际上是输出 HTML 中 title 节点的文本内容。所以, 通过soup. title 选出HTML 中的 title节点, 再调用string属性就可以得到title节点里面的文本了。
5、提取信息
前面,演示了通过string属性获取文本的值,那么如何获取节点名称?如何获取节点属性的值呢?接下来统一来梳理一下信息的提取方式吧!下面统一以提取下面的html信息做示例:
html= """
<html><head><title>The Dormouse's story</title></head><body>
<p class="title" name="dromouse"><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>
"""
(1)获取名称
利用name属性可以获取节点的名称。先选取title节点,再调用name属性就可以得到节点名称:
print(soup.title.name)
运行结果为:
title
(2)获取属性
一个节点可能有多个属性,例如id和class等。可以选择该节点调用其attrs获取其所有属性,但attrs属性的返回结果是字典形式,包括所选择节点的所有属性和属性值。因此要获取name属性,相当于从字典中获取某个键值。
print(soup.p.attrs)
print(soup.p.attrs['name'])
运行结果为:
{'class': ['title'], 'name': 'dromouse'}
dromouse
其实还有一种更为简单的方法,不用写attrs,直接在节点元素后面传入需要的属性名即可。例:
print(soup.p['name'])
print(soup.p['class'])
结果为:
dromouse
['title']
(3)获取内容
利用string属性获取节点元素包含的文本内容,获取的是第一个p节点。
print(soup.p.string)
结果为:
The Dormouse's story
(4)嵌套选择
在html中存在节点嵌套情况,假如:获取了head节点,就可以继续调用head选取其内部的head节点。
html= """
<html><head><title>The Dormouse's story</title></head><body>
<p class="title" name="dromouse"><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>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.head.title)
print(soup.head.title.string)
结果为:
<title>The Dormouse's story</title>
The Dormouse's story
这里是head节点中嵌套了title点,通过.来选择节点的嵌套节点。
(5)关联选择
有时候不能一步就选到想要的节点,需要先选中某一个节点,再以它为基准选子节点、父节点、兄弟节点。
- 子节点和子孙节点
选取节点后,想要获取它的直接子节点,可以调用contents属性,它返回的是列表形式的该节点的所有子节点 。如果想要子孙节点,可以用descendants属性,它返回的也是查询所有子节点,得到所有的子孙节点。
- 父节点和祖先节点
如果要获取某个节点元素的父节点,可以调用parent属性,其输出的是该节点的父节点的所有内容。 如果需要获取其所有祖先节点,可以调用parents属性。
- 兄弟节点
如果要获取同级节点,也就是兄弟节点该怎么办呢?使用next_sibling和previous_sibling分别用于获取节点的下一个和上一个兄弟节点,next_siblings和previous_siblings则分别返回后面和前面的所有兄弟节点。
(6)方法选择器
前面讲的方法都是通过基于属性来选择的,但比较繁琐,还有一种更好的查询方法。
- find_all
查询所有符合条件的元素,可以传入需要的属性或文本来得到满足条件的元素。API如下:
find_all(name,attrs,recursive,text,**kwargs)
name:根据元素或节点查询
attrs:根据属性值来查询
text:根据节点的文本来查询(可以是字符串或正则表达式)
- find
find_all是返回所有匹配的元素组成的列表,find是返回第一个匹配的元素,用法与find_all相同。
另外还有许多查询方法, 用法与介绍过的 find_all、find完全相同, 区别在于查询范围不同, 在此做一下简单的说明。
- find_parents 和 find_parent: 前者返回所有祖先节点, 后者返回直接父节点。
- find_next_siblings 和 find_next_sibling: 前者返回后面的所有兄弟节点, 后者返回后面第一个兄弟节点。
- find_previous_siblings 和 find_previous_sibling: 前者返回前面的所有兄弟节点, 后者返回前面第一个兄弟节点。
- find_all_next 和 find_next:前者返回节点后面所有符合条件的节点,后者返回后面第一个符合条件的节点。
- find_all_previous 和 find_previous: 前者返回节点前面所有符合条件的节点, 后者返回前面第一个符合条件的节点。
6、CSS选择器
如果你熟悉Web开发,那么对CSS肯定不陌生。使用CSS选择器,只需要调用select方法,传入相应的CSS选择器即可。通过一个实例感受一下:
html = '''
<div class="panel"><div class="panel-heading"><h4>Hello</h4></div><div class="panel-body"><ul class="list" id="list-1"><li class="element">Foo</li><li class="element">Bar</li><li class="element">Jay</li></ul><ul class="list list-small" id="list-2"><li class="element">Foo</li><li class="element">Bar</li></ul></div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
结果如下:
[<div class="panel-heading">
<h4>Hello</h4>
</div>]
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>]
第一个打印的是class为panel节点的中的class为panel-heading的节点信息,第二个打印的是ul节点中的li节点信息,第三个打印id为list-2中class为elment的元素信息。
7、总结
- 推荐使用lxml解析库,必要时用html.parser
- 节点选择器筛选功能弱,但速度快
- 建议用find和find_all方法查询匹配的单个结果或多个结果
- 如果对CSS选择器熟悉,则可以用select选择法。
这篇关于网页数据的解析提取(Beautiful Soup库详解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!