单线程+多任务异步协程完成爬虫任务
### - 单线程+多任务异步协程
异步进程主要是又三点构成:特殊函数所谓的特殊函数就是被async关键字所修饰的函数,这个函数的返回值由回调函数拿到,协成创建指定的协成对象,实例化任务对象
- 特殊的函数
- 被async关键字修饰的函数定义,该函数就是一个特殊的函数
- 特殊之处:
- 特殊函数被调用后,函数定义的内部实现语句没有被立即执行
- 该函数会返回一个协程对象
- 特殊函数 == 一组指定形式的操作 == 协程对象
- 协程对象 == 一组指定形式的操作
- 协程
- 创建方式:通过特殊函数调用返回
- 协程对象 == 一组指定形式的操作
- 任务对象
- 任务对象就是一个高级的协程对象
- 任务对象 == 协程对象 == 一组指定形式的操作
- 任务对象 == 一组指定形式的操作
- 任务对象的创建:
- ```
asyncio.ensure_future(c)#这个参数c是实例化的协成对象
```
- 给任务对象绑定回调函数:
#定义回调函数
def parse(t):#必须且只能有一个参数(回调函数的调用者表示的那一个任务对象)
result = t.result() #result()返回的就是参数t表示的任务对象对应的特殊函数中的返回值
print(result)
task.add_done_callback(parse) #给任务对象绑定回调函数
- 事件循环对象
- 作用:充当一个容器,用来装在任务对象。当事件循环启动后,就可以异步的执行其内部装载的一个或多个任务对象。
- 创建:loop = asyncio.get_event_loop()
- 启动加载:loop.run_until_complete(task)
- 完整实现:
-
import asyncio import time #特殊函数的定义 async def get_request(url): print('正在请求url:',url) time.sleep(2) print('请求结束:',url) return 123 #定义回调函数 def parse(t):#必须且只能有一个参数(回调函数的调用者表示的那一个任务对象) result = t.result() #result()返回的就是参数t表示的任务对象对应的特殊函数中的返回值 print(result) #协程对象 c = get_request('www.1.com') #任务对象 task = asyncio.ensure_future(c) task.add_done_callback(parse) #给任务对象绑定回调函数 #事件循环对象 loop = asyncio.get_event_loop() #创建一个事件循环对象 loop.run_until_complete(task) #将一个任务对象装载在loop对象中,切启动了时间循环对象
- 多任务的异步效果的体现
- wait()方法:可以将tasks列表中的每一个任务对象赋予可被挂起的权限。
- 挂起:让当前的任务对象交出cpu的使用权。
- 注意:在特殊函数的实现内部,不可以出现不支持异步模块的代码,否则会中断整个异步效果!
- await关键字:保证阻塞操作一定会被执行!、
import asyncio
import time
start = time.time()
urls = [
'www.1.com',
'www.2.com',
'www.3.com'
]
#特殊函数的定义
async def get_request(url):
print('正在请求url:',url)
await asyncio.sleep(2)
print('请求结束:',url)
return 123
tasks = []
for url in urls:
c = get_request(url)
task = asyncio.ensure_future(c)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start)
- 爬取自己服务器里的数据,实现异步效果 Server.py
from flask import Flask,render_template
from time import sleep
app = Flask(__name__)
@app.route('/bobo')
def index1():
sleep(2)
return render_template('test.html')
@app.route('/jay')
def index2():
sleep(2)
return render_template('test.html')
@app.route('/tom')
def index3():
sleep(2)
return render_template('test.html')
if __name__ == '__main__':
app.run(debug=True)
爬虫端的代码:
import asyncio
import requests
import time
from lxml import etree
start = time.time()
urls = ['http://127.0.0.1:5000/bobo',
'http://127.0.0.1:5000/jay',
'http://127.0.0.1:5000/tom',]
async def get_request(url):
response = requests.get(url)
page_text = response.text
return page_text
def parse(task):
page_text = task.result()
tree = etree.HTML(page_text)
data = tree.xpath('//body/text()')[0]
print(data)
tasks = []
for url in urls:
c = get_request(url)
task = asyncio.ensure_future(c)
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来代替requests
#####aiohttp:
pip install aiohttp
###编码流程:编写大致架构
```python
async def get_request(url):
with aiohttp.ClientSession() as sess:#创建了一个请求对象
with sess.get(url) as response: #发起请求,返回响应对象
page_text = response.text() #read()返回byte类型
return page_text
补充细节
- 在每个with前加上async关键字
在每一步阻塞前加上await关键字
async def get_request(url):
async with aiohttp.ClientSession() as sess:#创建了一个请求对象
async with await sess.get(url) as response: #发起请求,返回响应对象
page_text = await response.text() #read()返回byte类型
return page_text
完整操作
import asyncio
import aiohttp
import time
from lxml import etree
start = time.time()
urls = ['http://127.0.0.1:5000/bobo',
'http://127.0.0.1:5000/jay',
'http://127.0.0.1:5000/tom',]
async def get_request(url):
async with aiohttp.ClientSession() as sess:#创建了一个请求对象
async with await sess.get(url) as response: #发起请求,返回响应对象
page_text = await response.text() #read()返回byte类型
return page_text
def parse(task):
page_text = task.result()
tree = etree.HTML(page_text)
data = tree.xpath('//body/text()')[0]
print(data)
tasks = []
for url in urls:
c = get_request(url)
task = asyncio.ensure_future(c)
task.add_done_callback(parse)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start)