目的:在爬虫中使用异步实现高性能的数据爬取操作
方式
多线程、多进程(不建议)
好处:可以为相关阻塞的操作单独开启进程或者线程,阻塞操作就可以异步执行
弊端:无法无限制的开启多线程或者多进程
线程池、进程池(适当使用)
好处:我们可以降低系统对进程或者线程创建和销毁的一个频率,从而更好地降低系统的开销
弊端:池中线程或者进程的数量是有上限
实例:下载视频
# 爬取视频的视频数据
import requests
from lxml import etree
import re
from multiprocessing.dummy import Pool
# 原则:线程池处理的是阻塞且耗时的操作
# 对下述url发起请求解析出视频详情页的url和视频的名称
if __name__ == '__main__':
# 人物页url
homr_url = 'https://www.pearvideo.com/category_1'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54'
}
# 获取该页的响应数据
page_text = requests.get(url=homr_url, headers=headers).text
# 实例化etree对象
tree = etree.HTML(page_text)
# 获取该页中跳转到视频页的地址
mp4_text = tree.xpath('//*[@id="listvideoListUl"]/li/div/a/@href')
# 同时获取对应的名称
mp4_title = tree.xpath('//*[@id="listvideoListUl"]/li/div/a/div[2]/text()')
# 建立空列表,后面用来芳名称和链接
urls = []
i = 0
for src in mp4_text:
# 拼接跳转到视频页的链接
page_src = 'https://www.pearvideo.com/' + src
# 从中取出contId
contld = page_src.split('_')[-1]
# page_src是referer,contld是contId,必须要有这俩才能获取到相应的视频假链接
# print(contld)
headers = {
'referer': page_src,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54'
}
params = {
'contId': contld
}
# 获取视频假链接
mp4_url = 'https://www.pearvideo.com/videoStatus.jsp'
mp4 = requests.get(url=mp4_url, headers=headers, params=params).text
# 利用正则来找更
ex = '"srcUrl":"(.*?)"'
securl = re.findall(ex, mp4)[0]
# 对链接进行分割和拼接,得到真的视频链接
url = securl.split('-')
new_url = 'https://video.pearvideo.com/mp4/short/20180313/cont-' + str(contld) + '-' + url[-2] + '-' + url[-1]
# 将名称和链接以字典形式保存,并放进列表里
dic = {
'name': mp4_title[i],
'url': new_url
}
urls.append(dic)
i += 1
print(urls)
# 下载视频的封装函数
def get_video_data(dic):
url = dic['url']
print(dic['name'] + '正在下载...')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54'
}
mp4_data = requests.get(url=url, headers=headers).content
filename = '梨视频/' + dic['name'] + '.mp4'
# 持久化保存
with open(filename, 'wb') as fp:
fp.write(mp4_data)
print(dic['name'] + '下载成功')
# 创建进程池
pool = Pool(4)
pool.map(get_video_data, urls)
pool.close()
pool.join()
单线程+异步协程(推荐)
event_loop: 事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上当满足某些条件的时候,函数就会被循环执行。
coroutine:协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用。async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回我们可以使用个协程对象
task: 任务,它是对协程对象的进一步封装,包含了任务的各个状态
future: 代表将来执行或还没有执行的任务,实际上和 task 没有本质区别
async 定义一个协程
await 用来挂起阻塞方法的执行
requests.get是基于同步,必须使用基于异步的网络请求模块进行指定url的请求发送,若是用get还是6s,换成异步会变成2s
![](https://i-blog.csdnimg.cn/blog_migrate/b85dae4b66b7de83d45de065a41a2cc8.png)
aiohttp:基于异步网络请求的模块
pip install aiohttp
使用方法:变为2s
![](https://i-blog.csdnimg.cn/blog_migrate/cd0cdb52c7e3764dc0c6beb51930ca72.png)
协程
协程不是计算机提供,是程序员人为创造
协程也可以被称为微线程,是一种用户态内的上下文切换技术,简而言之,就是通过一个线程实现代码块相互切换执行
协程意义
在一个线程中如果遇到IO等待时间,线程不会傻傻等待,会利用空闲的时候干点其他事