1. bs4基本使用
1.1. 简介
bs4的全称为 BeautifulSoup
。和lxml一样,是一个html的解析器,主要功能也是解析数据和提取数据 。
本模块作为了解模块,实际开发中很少用这个模块去解析数据,大家可能会想为什么这个模块会逐渐被淘汰,它就真的一点优点都没有吗?优点吧其实也有,比如设计的接口比较人性化,使用起来比较方便,支持css选择器等等,但是缺点也是非常致命的,就是它的效率是没有 xpath 高,程序界都是很讲效率的,那既然 xpath 也很好用,效率也高,自然而然就没有bs4的市场了,但是大家也要了解该模块,可以不用,但是不能不知道。
1.2. 安装
pip install bs4
这里需要注意的一点是,如果你的项目是一个全新的项目文件,你只安装上bs4
也是用不了该模块的解析方法的,还需要下载安装一个lxml
库,因为bs4
是依赖lxml
的,不然是会报错的。都有lxml
库了,正常都使用xpath
了对吧,谁还去特意再安装bs4
,这也是bs4
被淘汰的一点因素。
安装成功之后想要在python程序当中使用的话就直接导入该模块即可。
from bs4 import BeautifulSoup
1.3. 基础语法
<!DOCTYPE html>
<html lang="en" xmlns=""><head><meta charset="UTF-8"><title>Title</title></head><body><div><ul><li id="l1" class="a1">张三</li><li id="l2">李四</li><li>王五</li><a id="Hello" href="" class="a1">Hello World</a><a id="Hello1" href="" class="a1">Hello 美女</a><span>嘿嘿嘿</span></ul></div><a href="https://www.baidu.com" title="a2">百度</a><a href="https://www.xiaomi.com" title="a3">小米</a><div id="d1"><span><h1>哈哈哈</h1></span></div><p id="p1" class="p1">呵呵呵</p><a href="">京东</a></body>
</html>
from bs4 import BeautifulSoupsoup = BeautifulSoup(open('index.html', encoding='utf8'), 'lxml')
当操作的对象是文件时,就可以使用open('文件路径', encoding='utf8')
以指定编码打开文件进行操作,当操作对象就是我们获取的响应时,就可以更改为BeautifulSoup(response, 'lxml')
,lxml
是操作内核,也有其他的内核,但是lxml
内核效率更高。
1.3.1.节点定位
from bs4 import BeautifulSoupsoup = BeautifulSoup(open('index.html', encoding='utf8'), 'lxml')# 1.根据标签名进行查找
print(soup.a) # 获取到第一个a标签 类型为 <class 'bs4.element.Tag'>
# 输出结果为:<a class="a1" href="" id="Hello">Hello World</a>print(soup.a.attrs) # 获取到第一个a标签的属性值 字典类型
# 输出结果为:{'id': 'Hello', 'href': '', 'class': ['a1']}# 2.函数方法 find() 和 find_all()
print(soup.find('a')) # 等同于soup.a
#输出结果为: <a class="a1" href="" id="Hello">Hello World</a>
print(type(soup.find('a'))) # <class 'bs4.element.Tag'>print(soup.find_all('a')) # 获取所有的a标签,返回的是一个列表
# 输出结果为:[<a class="a1" href="" id="Hello">Hello World</a>, <a class="a1" href="" id="Hello1">Hello 美女</a>, <a href="https://www.baidu.com" title="a2">百度</a>, <a href="https://www.xiaomi.com" title="a3">小米</a>, <a href="">京东</a>]print(soup.find('a', title="a2")) # 获取title属性值为a2的a标签
# 输出结果:<a href="https://www.baidu.com" title="a2">百度</a>print(soup.find('a', class_="a1")) # 获取class值为a1的a标签,class属性需要在最后面加上_
# 输出结果为:<a class="a1" href="" id="Hello">Hello World</a>print(soup.find_all('a', class_="a1")) # 获取到所有class值为a1的a标签
# 输出结果为:[<a class="a1" href="" id="Hello">Hello World</a>, <a class="a1" href="" id="Hello1">Hello 美女</a>]print(soup.find_all(['a', 'p'])) # 同时获取多个标签,这里是同时获取所有a标签和p标签
# 输出结果为:[<a class="a1" href="" id="Hello">Hello World</a>, <a class="a1" href="" id="Hello1">Hello 美女</a>, <a href="https://www.baidu.com" title="a2">百度</a>, <a href="https://www.xiaomi.com" title="a3">小米</a>, <p class="p1" id="p1">呵呵呵</p>, <a href="">京东</a>]# 3.select() css选择器
print(soup.select('a')) # 获取所有a标签 输出结果与.find_all('a')相同print(soup.select('#l2')) # 获取id属性值为l2的标签,返回值为列表,哪怕结果只有一个
# 输出结果:[<li id="l2">李四</li>]print(soup.select('.a1')) # 获取所有class属性值为a1的标签
# 输出结果:[<li class="a1" id="l1">张三</li>, <a class="a1" href="" id="Hello">Hello World</a>, <a class="a1" href="" id="Hello1">Hello 美女</a>]# 属性选择器
print(soup.select('a[class="a1"]')) # 获取class属性值为a1的a标签,等同于.find_all('a', class_="a1"))
# 输出结果:[<a class="a1" href="" id="Hello">Hello World</a>, <a class="a1" href="" id="Hello1">Hello 美女</a>]print(soup.select('a[class]')) # 获取有class属性的a标签
# 输出结果:[<a class="a1" href="" id="Hello">Hello World</a>, <a class="a1" href="" id="Hello1">Hello 美女</a>]# 层级选择器
print(soup.select('div li')) # 获取div标签下面的所有后代li标签 后代选择器 可跨多个节点
# 输出结果为:[<li class="a1" id="l1">张三</li>, <li id="l2">李四</li>, <li>王五</li>]print(soup.select('div > ul > li')) # 获取div标签下的子代ul标签下的子代li节点 子代选择器 只能一级一级向下递
# 输出结果为:[<li class="a1" id="l1">张三</li>, <li id="l2">李四</li>, <li>王五</li>]print(soup.select('span, p')) # 获取所有的span标签和p标签
# 输出结果为:[<span>嘿嘿嘿</span>, <span>
# <h1>哈哈哈</h1>
# </span>, <p class="p1" id="p1">呵呵呵</p>]
1.3.2. 节点信息
from bs4 import BeautifulSoupsoup = BeautifulSoup(open('index.html', encoding='utf8'), 'lxml')obj = soup.select('li[id="l1"]')[0] # 获取di属性值为l1的li节点
print(obj) # <li class="a1" id="l1">张三</li>
print(type(obj)) # <class 'bs4.element.Tag'>
获取文本
现在节点是拿到了,那如果想要获取文本信息或者属性值又该如何获取呢?获取文本信息有两种方法。obj.string
与obj.get_text()
。
看起来是一样的功能一样的效果,但其实也是有区别的。
from bs4 import BeautifulSoupsoup = BeautifulSoup(open('index.html', encoding='utf8'), 'lxml')obj = soup.select('div[id="d1"]>span')[0] # 获取id值为d1的div下面的子代span标签
print(obj)
这个span标签内部包裹了h1标签,文本值是在h1标签里面包裹的,这个时候再使用上述两个方法获取文本值。
是不是发现不同的地方了,也就是说,当文本值不是当前标签直接包含的时候,.string
方法获取不到值,就会返回None
,而.get_text()
则是不管包裹到当前标签的哪一个后代标签里面,都会把文本值拿到。
获取属性值
from bs4 import BeautifulSoupsoup = BeautifulSoup(open('index.html', encoding='utf8'), 'lxml')obj = soup.select('a[title="a2"]')[0] # 获取title值为a2的a标签
print(obj)
# 输出结果为:<a href="https://www.baidu.com" title="a2">百度</a>print(obj.attrs)
# 输出结果为:{'href': 'https://www.baidu.com', 'title': 'a2'} 字典
可以看到,.attrs
属性值返回的是标签所有的属性值字典,需要取哪个具体值就直接使用字典取值方式取值即可,也就是obj.attrs.get("href")
,也有其他的快捷取值方法。
...
print(obj.attrs.get("href"))
print(obj.get("href"))
print(obj["href"])
三种方式都能获取到,喜欢用哪种就用哪种。