多任务异步协程asyncio
特殊函数:
- 就是async关键字修饰的一个函数的定义
- 特殊之处:
- 特殊函数被调用后会返回一个协程对象
- 特殊函数调用后内部的程序语句没有被立即执行
- 协程
- 对象。协程==特殊的函数。协程表示的就是一组特定的操作。
- 任务对象
- 高级的协程(对协程的进一步的封装)
- 任务对象==协程==特殊的函数
- 任务对象==特殊的函数
- 绑定回调:
- task.add_done_callback(task)
- 参数task:当前回调函数对应的任务对象
- task.result():返回的就是任务对象对应的特殊函数的返回值
- 事件循环对象
- 创建事件循环对象
- 将任务对象注册到该对象中并且开启该对象
- 作用:loop可以将其内部注册的所有的任务对象进行异步执行
- 挂起:就是交出cpu的使用权。
await:被用做在特殊函数的内部,在被阻塞的时候
wait:给每一个任务赋予一个可被挂起的权限
#【重点】在特殊函数内部的实现中,不可以出现不支持异步的模块(例如time,requests)代码,如果出现了,则会中断整个的异步效果!!!
asyncio的使用
import asyncio
import time
from time import sleep
# 特殊函数
async def get_request(url):
print('正在下载: ',url)
sleep(2)
print('下载完毕: ',url)
return 'page_text'
# 回调函数,普通函数
def parse(task):
# 参数表示任务对象
print('i am callback',task.result())
start = time.time()
# 调用特殊函数
func = get_request('www.xx.com')
# 创建任务对象
task = asyncio.ensure_future(func)
# 给任务对象绑定回调函数
task.add_done_callback(parse)
# 创建一个事件循环对象
loop = asyncio.get_event_loop()
# 让loop执行一个任务
loop.run_until_complete(task)
print("总耗时:",time.time()-start) #总耗时: 2.0017831325531006
多任务协程
import asyncio
import time
# 特殊函数
async def get_request(url):
print('正在下载',url)
# time.sleep(2) 不支持异步的模块 会中断整个的异步效果
await asyncio.sleep(2)
print('下载完成',url)
return 'page_text'
def parse(task):
print(task.result())
start = time.time()
urls = ['www.xxx1.com','www.xxx2.com','www.xxx3.com']
tasks = [] #存放多任务
for url in urls:
# 调用特殊函数
func = get_request(url)
# 创建任务对象
task = asyncio.ensure_future(func)
# 给任务对象绑定回调函数
task.add_done_callback(parse)
tasks.append(task)
# 创建事件循环对象
loop = asyncio.get_event_loop()
# 执行任务
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start) #2.0015313625335693
aiohttp的使用
- requests一定是不支持异步
- aiohttp是一个支持异步的网络请求模块
- 环境安装
- 编码流程:
- 大致的架构:
with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
with s.get(url) as response:
#response.read()二进制(.content)
page_text = response.text()
return page_text
- 补充细节
- 在每一个with前加上async
- 需要在每一个阻塞操作前加上await
async with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
async with await s.get(url) as response:
#response.read()二进制(.content)
page_text = await response.text()
return page_text
异步协程爬虫案例
# 需求用多任务异步协程获取百度,搜狗,京东,淘宝的页面源码数据,并简单解析
import asyncio
import requests
import time
from lxml import etree
urls = ['https://www.baidu.com','http://www.taobao.com/','http://www.jd.com/','https://www.sogou.com/']
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
# 特殊函数
async def get_request(url):
print('正在下载',url)
page_text = requests.get(url,headers=headers).text
print(url,'下载完成')
return page_text
# 回调函数
def parse(task):
page_text = task.result()
tree = etree.HTML(page_text)
div = tree.xpath('//div')
print(div)
start = time.time()
tasks = []#存放多任务
for url in urls:
func = get_request(url)
task = asyncio.ensure_future(func)
task.add_done_callback(parse)
tasks.append(task)
# 创建事件要在循环外
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start)
#根据结果发现执行并不是异步,原因是requests不是异步模块,所以整个程序不会异步执行
基于aiohttp的多任务协程的爬虫
# 需求用多任务异步协程获取百度,搜狗,京东,淘宝的页面源码数据,并简答解析
import asyncio
import requests
import time
import aiohttp
from lxml import etree
urls = ['https://www.baidu.com','http://www.taobao.com/','http://www.jd.com/','https://www.sogou.com/']
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
# 特殊函数
async def get_request(url):
async with aiohttp.ClientSession() as s:
# s.get(url,headers,params,proxy="http://ip:port")
async with await s.get(url,headers=headers) as response:
print('正在下载', url)
# response.read()二进制(.content)
page_text = await response.text()
print(url, '下载完成')
return page_text
# 回调函数
def parse(task):
page_text = task.result()
tree = etree.HTML(page_text)
div = tree.xpath('//div')
print(div)
start = time.time()
tasks = []#存放多任务
for url in urls:
func = get_request(url)
task = asyncio.ensure_future(func)
task.add_done_callback(parse)
tasks.append(task)
# 创建事件要在循环外
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start) #总耗时: 3.0848371982574463