基本概念:
- 单线程+多任务异步协程:pip install asyncio
-
特殊的函数
- 如果一个函数的定义被async修饰后,则该函数就变成了一个特殊的函数
- 特殊之处:
- 该特殊的函数调用后,函数内部的实现语句不会被立即执行
- 该特殊函数被调用后会返回一个协程对象
-
协程对象
- 对象。通过特殊函数的调用返回一个协程对象。
- 协程 == 特殊函数 == 一组指定的操作
- 协程 == 一组指定的操作
-
任务对象
- 任务对象就是一个高级的协程对象。(任务对象就是对协程对象的进一步封装)
- 任务 == 协程 == 特殊函数 == 一组指定操作
- 任务 == 一组指定的操作
- 如何创建一个任务对象:
- asyncio.ensure_future(协程对象)
- 任务对象的高级之处:
- 可以给任务对象绑定回调:
- task.add_done_callback(task_callback)
- 回调函数的调用时机:
- 任务被执行结束后,才可以调用回调函数
- 回调函数的参数只可以有一个:表示的就是该回调函数的调用者(任务对象)
- 使用回调函数的参数调用result()返回的就是任务对象表示的特殊函数return的结果
- 可以给任务对象绑定回调:
-
事件循环对象
- 对象。
- 作用:
- 可以将多个任务对象注册/装载到事件循环对象中
- 如果开启了事件循环后,则其内部注册/装载的任务对象表示的指定操作就会被基于异步的被执行
- 创建方式:
- loop = asyncio.get_event_loop()
- 注册且启动方式:
- loop.run_until_complete(task)
-
wait方法的作用:
- 将任务列表中的任务对象赋予可被挂起的权限。只有任务对象被赋予了可被挂起的权限后,该任务对象才可以被挂起
- 挂起:将当前的任务对象交出cpu的使用权。
- 将任务列表中的任务对象赋予可被挂起的权限。只有任务对象被赋予了可被挂起的权限后,该任务对象才可以被挂起
-
注意事项【重要】:
- 在特殊函数内部不可以出现不支持异步模块对应的代码,否则会中断整个异步效果
-
await关键字
- 在特殊函数内部,凡是阻塞操作前都必须使用await进行修饰。await就可以保证
阻塞操作在异步执行的过程中不会被跳过!
- 在特殊函数内部,凡是阻塞操作前都必须使用await进行修饰。await就可以保证
-
多任务异步爬虫框架:
import requests
import asyncio
import time
import aiohttp
from lxml import etree
urls = [ # 用列表存储你想要爬取的网站
'http://www.1.com',
'http://www.2.com',
'http://www.3.com'
]
# requests是一个不支持异步的模块,需要使用aiohttp发起请求
# get_request()被async修饰后变成了一个特殊的函数
async def get_request(url):
#实例化好了一个请求对象
async with aiohttp.ClientSession() as sess:
#调用get发起请求,返回一个响应对象
#get/post(url,headers,params/data,proxy="http://ip:port")
async with await sess.get(url=url) as response:
#text()获取了字符串形式的响应数据
#read()获取byte类型的响应数据
page_text = await response.text()
return page_text
#解析函数的封装
#参数t:就是该回调函数的调用者(任务对象)
def parse(t):
#获取请求到页面源码数据
page_text = t.result() # result返回的就是特殊函数的返回值
tree = etree.HTML(page_text)
parse_text = tree.xpath('//a[@id="feng"]/text()')[0]
print(parse_text)
if __name__ == "__main__":
start = time.time()
tasks = []
for url in urls:
c = get_request(url) # c就是一个协程对象
task = asyncio.ensure_future(c) # 任务对象就是对协程对象的进一步封装
task.add_done_callback(parse) # 给task绑定一个回调函数
tasks.append(task)
loop = asyncio.get_event_loop() # 创建事件循环对象
loop.run_until_complete(asyncio.wait(tasks)) # 将任务对象注册到事件循环中且开启事件循环
print('总耗时:',time.time()-start)