更新日期: 2021.03.29
本节内容 :比较 bs4 和 re 解析器的解析速度。
目录
- 1. 测试目的
- 2. 测试方法
- 3. 测试文档及目标信息
- 4. 比较两种 re 方法的解析速度
- 4.1 每个 re 语句提取两个信息
- 4.2 每个 re 语句提取一个信息, 提取两次
- 5. 测试 bs4 在各种依赖下运行的速度
- 6. 测试 bs4 在同一依赖不同条件下的运行速度
- 7. 总结
- 7.1 re 比 bs4 慢很多
- 7.2 re
- 7.3 bs4
1. 测试目的
测试分析两个解析器的运行速度,结合代码的复杂程度及可读性,作为今后使用的参考。
2. 测试方法
- 比较两种 re 方法的解析速度 (一次提取两个 v.s. 分两次提取信息)
- 测试 bs4 在各种依赖下运行的速度
3. 测试文档及目标信息
慕课网免费课程首页在2021年3月22日的代码。(47429 字/1958 行)
网页链接:https://www.imooc.com/course/list
网页构成:页面的主要信息是40个课程的图片,名称,链接等信息,每个课程在一个 ‘a’ 标签内。
目标信息:每个课程的名称和链接。
代码样例:一个代码片段如下。课程名称在 line 726,链接 在 line 722。
一个坑… 使用 bs4 时,计划使用是否包含 class="item free " 来定位 ‘a’ 标签。
可是用 class_="item free " 什么都搜不到 (class 后面已经了下划线),copy 一行代码过来直接 soup 都不行…反复乱试, 终于, 把 free 和后面双引号之间的空格去掉时ok了…可能靓汤觉得这里有空格不规范就私下给改了…
4. 比较两种 re 方法的解析速度
4.1 每个 re 语句提取两个信息
运行一次获得80个信息 (40个元组 * 每组两个信息)。
- 运行10次15.095056772232056秒
- 运行100次157.56561970710754…
import re
import time
start_time = time.time() # 记录开始时间
for i in range (0,100): # 循环100次测试解析时间
course_name_link = re.findall('.*?item free.*?href="//(.*?)".*?title="(.*?)"', doc, re.S)
print(len(course_name_link)) # 确认获得了多少个目标信息
print(time.time()-start_time) # 输出运行时间
4.2 每个 re 语句提取一个信息, 提取两次
运行一次获得80个信息 (40个元组 * 每组两个信息)。
- 运行10次16.77993679046631秒
- 运行100次…我不想知道了…
import re
import time
start_time = time.time() # 记录开始时间
for i in range (0,10): # 循环10次测试解析时间
course_name = re.findall('.*?title="(.*?)"', doc, re.S)
course_link = re.findall('.*?item free.*?href="//(.*?)"', doc, re.S)
print(len(course_name),len(course_link)) # 确认获得了多少个目标信息
print(time.time()-start_time)
分成两次解析仅多花费了一点时间, 我觉得分开写比较清楚。
re 的运行时间应该和网页代码的复杂度以及搜索模式有关系, 改一下试试, 如下。一次15.764955520629883秒… 可能因为这段代码里的 ‘href’ 比较多, 因此要求解析器先找到它比找到 ‘item free’ 花费更多的时间。
# before
course_name_link = re.findall('.*?item free.*?href="//(.*?)".*?title="(.*?)"', doc, re.S)
# after.....
course_name_link = re.findall('.*?href="//(.*?)".*?title="(.*?)"', doc, re.S)
5. 测试 bs4 在各种依赖下运行的速度
使用 ‘lxml-xml’ 和 ‘xml’ 依赖没有解析到目标信息, 不管啦~
运行1000次, 获取80,000个信息, 这三个依赖比较下来 lxml 快一些。
不过, 如果是爬取网页, sleep 时间我都会设置在 > 1 秒, 然后, 一个页面需要解析的信息也没有那么多,所以,使用哪个 bs4 解析器对我来说都差不多。
from bs4 import BeautifulSoup
import time
start_time = time.time()
soup = BeautifulSoup(doc, "lxml") # 更改lxml, 分别测试多种依赖
course_link = []
course_name = []
for i in range (0,1000):
for tag in soup.find_all("a", class_="item free"):
course_link.append("https:"+ tag['href'])
course_name.append(tag['data-title'])
print(len(course_name), len(course_link))
print(time.time()-start_time)
6. 测试 bs4 在同一依赖不同条件下的运行速度
条件一:搜索符合 class_=“item free” 条件的标签,同时提供标签名字。
1000次,获得 40000 + 40000个数据,运行3.06秒,运行10000次,运行34.35秒。
条件二: 搜索符合 class_=“item free” 条件的标签,不提供标签名字。
1000次,获得 40000 + 40000个数据,运行6.09秒,运行10000次,运行63.53秒。
因此,在搜索时应尽可能提供准确的条件。
soup.find_all("a", class_="item free")
soup.find_all(class_="item free")
7. 总结
7.1 re 比 bs4 慢很多
- 也许,这段测试代码不适合re ~~
- 可能因为 bs4 直达目标标签,而勤恳的 re 还在逐字扫描…不过,在需要精准获取目标信息时,如获取字符串中的数字,就必须 re~~
7.2 re
- re 的搜索模式编译质量对运行时间影响很大,注意 ~~
7.3 bs4
- bs4 在几个不同依赖下运行的速度有点差别,不过从我的需求来看差不多,就用 lxml 吧,不行再换其他的,哈哈~~
- bs4 的搜索模式编译质量对运行时间影响很大,注意 ~~