我是靠谱客的博主 忧郁硬币,这篇文章主要介绍Python爬虫2.1 — BeautifulSoup用法教程,现在分享给大家,希望可以做个参考。

Python爬虫2.1 — BeautifulSoup用法教程

    • 综述
    • BeautifulSoup 介绍
      • 解析器
      • 几大解析工具的对比
    • 使用方法
      • 节点选择器
        • 选择元素
        • 提取信息
        • 嵌套选择
        • 关联选择
      • 方法选择器
        • find_all()
        • find()
      • CSS选择器
    • 总结
    • 其他博文链接

综述

本系列文档用于对Python爬虫技术的学习进行简单的教程讲解,巩固自己技术知识的同时,万一一不小心又正好对你有用那就更好了。
Python 版本是3.7.4

前面四篇文章讲了urllibrequests两个库的用法,这两个库主要是进行访问网站进行爬取数据,但是由于爬取下来的数据有很多,我们想要的只是其中的一部分,所以下面我们开始讲解如何从爬取的数据中提取我们想要的。

BeautifulSoup 介绍

BeautifulSoup是一个Python库,和lxml一样,BeautifulSoup也是一个HTMLXML的解析器,主要的功能也是如何解析和提取HTMLXML数据。lxml只会局部遍历,而BeautifulSoup是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。

BeautifulSoup用来解析HTML比较简单,API非常人性化,支持CSS选择器,Python标准库中的HTML解析器,也支持lxml的XML解析器。

要想使用BeautifulSoup首先要先安装这个库,安装方法如下:

复制代码
1
2
$ pip install beautifulsoup4

解析器

BeautifulSoup在解析HTMLXML的时候以来解析器,它除了支持Python标准库中的HTML解析器外,还支持一些第三方解析器(比如lxml),如下表所示:

解析器使用方法优势劣势
Python标准库BeautifulSoup(html,"html.parse")Python的内置标准库
执行速度适中
文档容错能力强
Python 2.7.3及Python 3.2.2之前的版本文档容错能力差
lxml HTML解析器BeautifulSoup(html, "lxml")速度快
文档容错能力强
需要安装C语言库
lxml XML解析器BeautifulSoup(html, "xml")速度快
唯一支持XML的解析器
需要安装C语言库
html5libBeautifulSoup(html, "html5lib")最好的容错性
以浏览器的方式解析文档
生成HTML5格式的文档
速度慢
不依赖外部扩展

建议使用lxml解析器,在后面,BeautifulSoup的用法实例也统一用这个解析器来演示。

几大解析工具的对比

解析工具解析速度使用难度
BeautifulSoup最慢最简单
lxml简单
正则表达式最快最难

使用方法

下面就以下列的一个html字符传作为例子进行使用方法介绍:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
html = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>BeautifulSoup学习</title> </head> <body> <div> <form name="form1" class="form1" action=""> <div class="text phone"> <input type="text" name="username" placeholder="请输入用户名"/> <p class="username">用户名不能为空</p> <p class="fu">有非法字符</p> </div> <div class="text phone"> <select name="area_code" id="area_code" style="line-height: 1.7rem;font-size: 90%;height: 1.6rem;padding-left: 5px;width: 80%;float: left;border-radius: 1.7rem;border: none;"> <option value="86">中国(+86)</option> </select> </div> <div class="text phone"> <input type="tel" name="mobile" placeholder="请输入手机号"/> <p class="phone-num">请输入正确的手机号</p> </div> <div class="text test"> <input type="text" name="mobile" placeholder="手机验证码"/> <button type="button" class="test-num">获取验证码</button> <p id="yan_num">请输入验证码</p> </div> <div class="text mima"> <input type="password" name="user_pwd" placeholder="输入登录密码"/> <p>请输入登录密码</p> </div> <div class="text again"> <input type="password" name="user_pwd_confirm" placeholder="再次确认密码"/> <p class="p1">请再次输入登录密码</p> <p class="p2">两次密码不一致</p> </div> <p class="text-phone">推荐人信息</p> <div class="text phone"> <input type="tel" name="tuijian" placeholder="推荐人手机号"/> <p class="tuijian">请输入正确的手机号</p> <p class="isYes">请输入推荐人手机号</p> </div> </form> </div> </body> </html> """

一个简单小例子:

复制代码
1
2
3
4
5
6
7
8
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # prettify()方法。这个方法可以把要解析的字符串以标准的缩进格式输出 print(soup.prettify()) print(soup.title.string)

首先,调用prettify()方法。这个方法可以把要解析的字符串以标准的缩进格式输出。(注意:对于不标准的HTML字符串BeautifulSoup,可以自动更正格式。)这一步不是由prettify()方法做的,而是在初始化BeautifulSoup时就完成了。

然后调用soup.title.string,这实际上是输出HTML中title节点的文本内容。所以,soup.title可以选出HTML中的title节点,再调用string属性就可以得到里面的文本了。

节点选择器

直接调用节点的名称就可以选择节点元素,再调用string属性就可以得到节点内的文本了,这种选择方式速度非常快。如果单个节点结构层次非常清晰,可以选用这种方式来解析。

选择元素

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 打印输出title节点选择结果 print(soup.title) # 输出它的类型,是bs4.element.Tag类型 print(type(soup.title)) # Tag具有一些属性,比如string属性,调用该属性,可以得到节点的文本内容 print(soup.title.string) # 选择head节点,打印节点加其内部的所有内容 print(soup.head) # 选择第一个p,也就是说,当有多个节点时,这种选择方式只会选择到第一个匹配的节点,其他的后面节点都会忽略 print(soup.p)

示例讲解:首先打印输出title节点的选择结果,输出结果正是title节点加里面的文字内容。接下来,输出它的类型,是bs4.element.Tag类型,这是BeautifulSoup中一个重要的数据结构。经过选择器选择后,选择结果都是这种Tag类型。Tag具有一些属性,比如string属性,调用该属性,可以得到节点的文本内容,所以接下来的输出结果正是节点的文本内容。

接下来,我们又尝试选择了head节点,结果也是节点加其内部的所有内容。最后,选择了p节点。不过这次情况比较特殊,我们发现结果是第一个p节点的内容,后面的几个p节点并没有选到。也就是说,当有多个节点时,这种选择方式只会选择到第一个匹配的节点,其他的后面节点都会忽略。

提取信息

上面演示了调用string属性来获取文本的值,那么如何获取节点属性的值呢?如何获取节点名呢?下面来统一梳理一下信息的提取方式。

  1. 获取名称
    可以利用name属性获取节点的名称,这里还是以上吗的文本为例,选区title节点,然后掉用name属性就可以得到节点名称:
复制代码
1
2
3
4
5
6
7
8
9
10
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 打印输出title节点的名称 print(soup.title.name) # 输出结果:title
  1. 获取属性
    每个节点可能有多个属性,比如idclass等,选择这个节点元素后,可以调用attrs获取所有属性:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 打印第一个input节点的信息 print(soup.input.attrs) print(soup.input.attrs['name']) # 输出结果: # {'type': 'text', 'name': 'username', 'placeholder': '请输入用户名'} # username

可以看到,attrs的返回结果是字典形式,它把选择的节点的所有属性和属性值组合成一个字典。接下来,如果要获取name属性,就相当于从字典中获取某个键值,只需要用中括号加属性名就可以了。比如,要获取name属性,就可以通过attrs['name']来得到。

其实这样有点烦琐,还有一种更简单的获取方式:可以不用写attrs,直接在节点元素后面加中括号,传入属性名就可以获取属性值了。样例如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 打印第一个input节点的信息 print(soup.input['name']) print(soup.input.attrs['placeholder']) # 输出结果: # username # 请输入用户名

【注意】这里需要注意的是,有的返回结果是字符串,有的返回结果是字符串组成的列表。比如,name属性的值是唯一的,返回的结果就是单个字符串。而对于class,一个节点元素可能有多个class,所以返回的是列表。在实际处理过程中,我们要注意判断类型。

  1. 获取内容
    可以利用string属性获取节点元素包含的文本内容,比如要获取第一个p节点的文本:
复制代码
1
2
3
4
5
6
7
8
9
10
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') print(soup.title.string) # 输出结果: # BeautifulSoup学习

嵌套选择

在上面的例子中,我们知道每一个返回结果都是bs4.element.Tag类型,它同样可以继续调用节点进行下一步的选择。比如,我们获取了head节点元素,我们可以继续调用head来选取其内部的head节点元素,示例如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') print(soup.head.title) print(soup.head.title.string) # 输出结果: # <title>BeautifulSoup学习</title> # BeautifulSoup学习

关联选择

在做选择的时候,有时候不能做到一步就选到想要的节点元素,需要先选中某一个节点元素,然后以它为基准再选择它的子节点、父节点、兄弟节点等,这里就来介绍如何选择这些节点元素。

  1. 子节点和子孙节点
    • contents获取所选取节点元素的所有直接子节点
    • children获取所选取节点元素的所有直接子节点
    • descendants获取所选取节点元素的所有的子孙节点

代码示例如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 选取head节点元素之后 # 获取它的直接子节点,可以调用contents属性 print(soup.head.contents) # 调用children属性得到相应的结果,获得所有的直接子节点,是个list对象,可使用循环输出 print(soup.head.children) # 如果要得到所有的子孙节点的话,可以调用descendants属性: print(soup.head.descendants)
  1. 父节点和祖先节点
    • parent获取所选取节点元素的父节点
    • parents获取所选取节点元素的所有祖先节点

示例代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 选取第一个p节点元素之后 # 获取它的父节点,可以调用parent属性 print(soup.p.parent) # 调用parents属性得到相其所有祖先节点 print(soup.head.parents)
  1. 兄弟节点
    • next_sibling获取所选取节点元素的下一个兄弟节点
    • previous_sibling获取所选取节点元素的上一个兄弟节点
    • next_siblings获取所选取节点元素前面的所有的兄弟节点
    • previous_siblings获取所选取节点元素后面的所有的兄弟节点

方法选择器

前面所讲的选择方法都是通过属性来选择的,这种方法非常快,但是如果进行比较复杂的选择的话,它就比较烦琐,不够灵活了。幸好,BeautifulSoup还为我们提供了一些查询方法,比如find_all()find()等,调用它们,然后传入相应的参数,就可以灵活查询了。

find_all()

find_all(),顾名思义,就是查询所有符合条件的元素。给它传入一些属性或文本,就可以得到符合条件的元素,它的功能十分强大。

复制代码
1
2
find_all(name, attrs={}, recursive=True, text=None, limit=None, **kwargs)
  • name:查询节点的名称
  • attrs:查询节点的属性
  • recursive:是否进行递归查找
  • text:参数可用来匹配节点的文本,传入的形式可以是字符串,可以是正则表达式对象
  • limit:限制查询数量

代码示例如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re # 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 查询出所有的div div = soup.find_all(name='div') print(div) print(len(div)) # 查询出所有class名有phone的div,限制查询出2个 div_phone = soup.find_all(name='div', attrs={'class': 'phone'}, limit=2) # 对于一些常用的属性,比如id和class等,我们可以不用attrs来传递。比如,要查询id为list-1的节点,可以直接传入id这个参数 # div_phone = soup.find_all(name='div', class_='phone') print(div_phone) print(len(div_phone)) # 查询出所有内容带有手机号的p标签节点 p = soup.find_all(name='p', text=re.compile('手机号')) print(p)

find()

除了上面的find_all()方法,还有find()方法,只不过后者返回的是单个元素,也就是第一个匹配的元素,而前者返回的是所有匹配的元素组成的列表,用法基本一致(find()没有limit参数)。

代码示例如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 查询第一个匹配的div div = soup.find(name='div') print(div) # 查询出第一个class名有phone的div div_phone = soup.find(name='div', class_='phone') print(div_phone) inp = soup.find(name='input', attrs={'name': 'user_pwd'}) print(inp, inp['type'])

另外,还有许多查询方法,其用法与前面介绍的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():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。

CSS选择器

BeautifulSoup还提供了另外一种选择器,那就是CSS选择器。使用CSS选择器时,只需要调用select()方法,传入相应的CSS选择器即可。

CSS选择器教程文档:https://www.w3school.com.cn/cssref/css_selectors.asp

示例代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
# 引入bs4库 from bs4 import BeautifulSoup # 构造解析对象 soup = BeautifulSoup(html, 'lxml') # 查询标签class为phone的元素 print(soup.select('.phone')) print(soup.select('.again')) # 查询标签id为phone的元素 print(soup.select('#area_code')) # 查询标签class为again的div元素下的p元素 print(soup.select('div.again p'))

总结

到此,BeautifulSoup的用法基本就介绍完了,最后做一下简单的总结:

  • 推荐使用lxml解析库,必要时使用html.parser。
  • 节点选择筛选功能弱但是速度快。
  • 建议使用find()或者find_all()查询匹配单个结果或者多个结果。
  • 如果对CSS选择器熟悉的话,可以使用select()方法选择。

其他博文链接

  • Python爬虫1.1 — urllib基础用法教程
  • Python爬虫1.2 — urllib高级用法教程
  • Python爬虫1.3 — requests基础用法教程
  • Python爬虫1.4 — requests高级用法教程

最后

以上就是忧郁硬币最近收集整理的关于Python爬虫2.1 — BeautifulSoup用法教程的全部内容,更多相关Python爬虫2.1内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(80)

评论列表共有 0 条评论

立即
投稿
返回
顶部