三种方式:
1.多进程多线程(不建议)
2.进程池或者线程池(适当)
3.单线程+异步协程(推荐)
多进程多线程
占用cpu资源,不建议使用
基于线程池的异步爬虫
from multiprocessing.dummy importPoolimporttimedefrequest(url):print('downloading...')
time.sleep(2)print('download!!!')
pool= Pool(3) #线程池
urls =['www.baidu.com','www.sogou.com','www.taobao.com']
start=time.time()
pool.map(request,urls)print('total times:',time.time()-start)
结果:
downloading...
downloading...
downloading...
download!!!
download!!!
download!!!
total times:2.0691184997558594
单线程+异步协程
-event_loop: 事件循环,相当于一个无限循环,我们可以把一些特殊的函数注册到事件循环中,当满足某些条件时,函数就会被执行.在编写异步程序时,必然有部分程序的耗时是比较久的,需要先让出当前程序的控制权,让其在背后运行,让后面的程序先运行起来.当背后运行的程序完成后,再通知主程序进行下一步操作.-coroutine: 中文翻译叫协程.在Python中常代指为协程对象类型.使用async关键字修饰一个普通函数,这个函数在调用时就不会立即执行,而是返回一个协程对象.我们就可以将协程对象(特殊函数)注册到事件循环中.-task: 任务,是对协程对象的进一步封装,包含了任务的各个状态.-future: 和task没有本质区别,创建方法不一样而已.- 另外我们还需要了解 async/await 关键字,它是从 Python 3.5 才出现的,专门用于定义协程。其中,async 定义一个协程,await 用来挂起阻塞方法的执行。
基本使用
importasyncio
asyncdefhello(name):print('hello to :',name)#获取了一个协程对象
c = hello('bobo')#创建一个事件循环对象
loop =asyncio.get_event_loop()#将协程对象注册到事件循环中,然后启动事件循环对象
loop.run_until_complete(c)
执行结果:
hello to : bobo
task的使用
importasyncio
asyncdefhello(name):print('hello to :',name)
c= hello('bobo')
loop=asyncio.get_event_loop()#就协程进行进一步的封装,封装到了task对象中
task = loop.create_task(c) #基于事件循环对象实现
print(task)
loop.run_until_complete(task)print(task)
执行结果::3>>hello to : bobo:3> result=None>
future的使用
importasyncio
asyncdefhello(name):print('hello to :',name)
c= hello('bobo')
task=asyncio.ensure_future(c)
loop=asyncio.get_event_loop()
loop.run_until_complete(task)
绑定回调
defcallback(task):print('i am callback:',task.result())importasyncio
asyncdefhello(name):print('hello to :',name)returnname
c= hello('bobo')
task=asyncio.ensure_future(c)#给任务对象绑定一个回调函数
task.add_done_callback(callback)
loop.run_until_complete(task)
执行结果:
hello to : bobo
i am callback: bobo
多任务异步协程
importasyncio
asyncdefrequest(url):print('正在下载:',url)
sleep(2) #非异步模块的代码:在此处如果存在非异步操作代码,则会彻底让asyncio失去异步的效果
print('下载成功:',url)
urls=['www.baidu.com','www.taobao.com','www.sogou.com']
start=time.time()
loop=asyncio.get_event_loop()
tasks= [] #任务列表,放置多个任务对象
for url inurls:
c=request(url)
task=asyncio.ensure_future(c)
tasks.append(task)#将多个任务对象对应的列表注册到事件循环中
loop.run_until_complete(asyncio.wait(tasks))print('总耗时:',time.time()-start)
执行结果:
正在下载: www.baidu.com
下载成功: www.baidu.com
正在下载: www.taobao.com
下载成功: www.taobao.com
正在下载: www.sogou.com
下载成功: www.sogou.com
总耗时:6.001343250274658
结果发现,并没有实现异步,是因为 time.sleep() 是非异步模块的代码,在协程对象中存在非异步操作代码,则会彻底让asyncio失去异步的效果
asyncio中也有sleep()方法,这个是异步的
importasyncio
asyncdefrequest(url):print('正在下载:',url)
await asyncio.sleep(2)print('下载成功:',url)
urls=['www.baidu.com','www.taobao.com','www.sogou.com']
start=time.time()
loop=asyncio.get_event_loop()
tasks= [] #任务列表,放置多个任务对象
for url inurls:
c=request(url)
task=asyncio.ensure_future(c)
tasks.append(task)#将多个任务对象对应的列表注册到事件循环中
loop.run_until_complete(asyncio.wait(tasks))print('总耗时:',time.time()-start)
执行结果:
正在下载: www.baidu.com
正在下载: www.taobao.com
正在下载: www.sogou.com
下载成功: www.baidu.com
下载成功: www.taobao.com
下载成功: www.sogou.com
总耗时:2.0011146068573 #生效了
多任务异步操作应用到爬虫中
因为影响爬虫效率的因素有很多,所以为了避免网速等因素的影响,单单测试多任务异步的效果,这里我们自己搭建一个服务器.
服务器端:
importrequests
asyncdefget_page(url):print('正在下载:',url)#之所以没有实现异步操作,原因是因为requests模块是一个非异步的模块
response = requests.get(url=url)print('响应数据:',response.text)print('下载成功:',url)
start=time.time()
urls=['http://127.0.0.1:5000/jack','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom']
tasks=[]
loop=asyncio.get_event_loop()for url inurls:
c=get_page(url)
task=asyncio.ensure_future(c)
tasks.append(task)
loop.run_until_complete(asyncio.wait(tasks))print('总耗时:',time.time()-start)
执行结果:
正在下载: http://127.0.0.1:5000/bobo
响应数据: Hello bobo
下载成功: http://127.0.0.1:5000/bobo
正在下载: http://127.0.0.1:5000/jay
响应数据: Hello jay
下载成功: http://127.0.0.1:5000/jay
正在下载: http://127.0.0.1:5000/tom
响应数据: Hello tom
下载成功: http://127.0.0.1:5000/tom
总耗时:6.0263447761535645 #无效,原因是因为requests模块是一个非异步的模块
这个时候就要使用另外一个支持异步的网络请求模块了:aiohttp
importaiohttpimportasyncio
asyncdefget_page(url):
async with aiohttp.ClientSession() as session:
async with await session.get(url=url) as response:
page_text= await response.text() #read() json()
print(page_text)
start=time.time()
urls=['http://127.0.0.1:5000/jack','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom','http://127.0.0.1:5000/jack','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom','http://127.0.0.1:5000/jack','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom']
tasks=[]
loop=asyncio.get_event_loop()for url inurls:
c=get_page(url)
task=asyncio.ensure_future(c)
tasks.append(task)
loop.run_until_complete(asyncio.wait(tasks))print('总耗时:',time.time()-start)
执行结果:
Hello jack
Hello jay
Hello tom
Hello jay
Hello jack
Hello tom
Hello jack
Hello tom
Hello jay
总耗时:2.031116008758545
如何实现数据解析--任务的绑定回调机制
importaiohttpimportasyncio#回调函数:解析响应数据
defcallback(task):print('this is callback()')#获取响应数据
page_text =task.result()print('在回调函数中,实现数据解析')
asyncdefget_page(url):
async with aiohttp.ClientSession() as session:
async with await session.get(url=url) as response:
page_text= await response.text() #read() json()
returnpage_text
start=time.time()
urls=['http://127.0.0.1:5000/jack','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom','http://127.0.0.1:5000/jack','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom','http://127.0.0.1:5000/jack','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom']
tasks=[]
loop=asyncio.get_event_loop()for url inurls:
c=get_page(url)
task=asyncio.ensure_future(c)#给任务对象绑定回调函数用于解析响应数据
task.add_done_callback(callback)
tasks.append(task)
loop.run_until_complete(asyncio.wait(tasks))print('总耗时:',time.time()-start)
执行结果:
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
thisiscallback()
在回调函数中,实现数据解析
总耗时:2.018115758895874