Python异步编程与aiohttp检测代理池
来源:互联网 发布:三星备份数据怎样恢复 编辑:程序博客网 时间:2024/05/18 00:37
之前为了检测代理的可用性学习了一下aiohttp,网上有关aiohttp的使用很少,所以写篇博客记录下来。
首先,为什么要使用异步编程,先看两张图。
很显然,我们不希望cpu在这些极慢的IO操作上阻塞,我们希望在IO操作期间,CPU能继续执行其他的任务,Python中的异步asyncio能很好实现这一点。
什么是协程?可以参考下面这篇文章
最新Python异步编程详解
aiohttp 简易使用教程
aiohttp基本使用可以参考aiohttp官网
在HTTP事物当中,当我们发送了一个GET请求后,我们的客户端与服务器建立TCP链接会有时延,链接建立后我们发送请求主体服务器接受后处理也会有时间延迟,将响应报文传送回来也会产生延迟,在同步模型中,程序在传输过程中会阻塞,等待传输完成,这将耗费大量的时间。
所以我们希望任何时候,当我们的客户端程序请求发送后,不因等待响应而阻塞,让程序能执行其他的事情,当响应到底后,以某种方法通知程序去处理,或者程序以查询的方式在空闲的时候回去查询这个响应是否到达。
Python的异步编程和异步网络框架可以帮我们做到这一点。
先上代码
import asynciofrom aiohttp import ClientSessionfrom ip_pool.MongoDB import mongodbimport requestsclass ProxyValidator(object): def __init__(self): self.url='https://www.zhihu.com/' self.headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36'} self.timeout=3.05 self.coro_count=500 self.proxyqueue=None self.useableProxy=set() async def _validator(self, proxy_queue): #测试代理的协程,受事件循环调度,传入公有待检测代理异步队列 if isinstance(proxy_queue,asyncio.Queue): async with ClientSession() as session: while not self.proxyqueue.empty(): try: proxy = await proxy_queue.get() proxystr = 'http://' + proxy['http'] async with session.get(self.url, headers=self.headers, proxy=proxystr, timeout=self.timeout) as resp: if resp.status == 200: # text=await resp.text() # print(text) # if '知乎 ' in text: # print(resp.headers) self.useableProxy.add(proxy['http']) print(proxystr) except asyncio.TimeoutError: pass except Exception as e: pass async def _get_proxyqueue(self): mongo=mongodb() proxy_iterator = mongo.find_from_mongodb({}, {'http': 1, '_id': 0}).skip(26000) proxyqueue=asyncio.Queue() for proxy in proxy_iterator: await proxyqueue.put(proxy) self.proxyqueue=proxyqueue return proxyqueue async def test_reportor(self): total=self.proxyqueue.qsize() time.clock() while not self.proxyqueue.empty(): total_lastsec=self.proxyqueue.qsize() await asyncio.sleep(1) validated_num=total_lastsec-self.proxyqueue.qsize() print('%d validated %d item/sec; useable proxy: %d ;%d item to validate' % ((total-self.proxyqueue.qsize()),validated_num,len(self.useableProxy),self.proxyqueue.qsize())) print('cost %f' % time.clock()) async def start(self): proxy_queue= await self._get_proxyqueue() to_validate=[self._validator(proxy_queue) for _ in range(self.coro_count)] to_validate.append(self.test_reportor()) await asyncio.wait(to_validate) def proxy_validator_run(self): # loop = asyncio.new_event_loop() # asyncio.set_event_loop(loop) loop=asyncio.get_event_loop() try: loop.run_until_complete(self.start()) except Exception as e: print(e)if __name__ == '__main__': validator=ProxyValidator() validator.proxy_validator_run()
这段异步的Http程序是如何运行的,我们一个方法一个方法来看。
def proxy_validator_run(self): # loop = asyncio.new_event_loop() # asyncio.set_event_loop(loop) loop=asyncio.get_event_loop() try: loop.run_until_complete(self.start()) except Exception as e: print(e)
这是这个类里面唯一没用使用协程的方法,在这个方法中,我们初始化了一个事件循环loop,并用Loop的run_until_complete()方法启动了协程self.start(),run_until_complete()方法接受一个协程或者像asyncio.wait(to_validate)这样经过asyncio.wait包装产生的期物,这里真的很难说清’期物’这个概念,具体的可以翻阅流畅的python这本书相关章节。
async def start(self): proxy_queue= await self._get_proxyqueue() to_validate=[self._validator(proxy_queue) for _ in range(self.coro_count)] to_validate.append(self.test_reportor()) await asyncio.wait(to_validate)
start被async语句包装成了协程,代码中就必须出现await与之对应,任何时候,你在等待什么,记住用await。这段代码中,将待检测代理队列的一个引用传递给proxy_queue,然后根据self.coro_count初始化了一个tasks对象列表to_validate,由于self._validator是一个协程,所以这个to_validate实际上一堆协程对象,通过asyncio.wait(to_validate)方法,我们等待to_validate完成。
也就是所的self._validator方法完成。
async def _validator(self, proxy_queue): #测试代理的协程,受事件循环调度,传入公有待检测代理异步队列
if isinstance(proxy_queue,asyncio.Queue):
async with ClientSession() as session:
while not self.proxyqueue.empty():
try:
proxy = await proxy_queue.get()
proxystr = 'http://' + proxy['http']
async with session.get(self.url, headers=self.headers,
proxy=proxystr, timeout=self.timeout) as resp:
if resp.status == 200:
# text=await resp.text()
# print(text)
# if '知乎 ' in text:
# print(resp.headers)
self.useableProxy.add(proxy['http'])
print(proxystr)
self._validator在这个方法中,async with session.get(self.url, headers=self.headers, proxy=proxystr, timeout=self.timeout) as resp:
还有 text=await resp.text() 都是靠aiohttp在底层实现了的异步代码,程序执行到这里,不会像同步模式一样等待响应,而是交出程序的控制权,控制权返回到事件循环loop后继续运行其他协程。这样我们便能在很短的时间内处理大量的http事务。
值得一提的是,win系统下并发数受制于文件句柄数512的最大限制,这段代码我启动了500个检测代理的协程交给事件循环loop调度运行,每秒检测数百个代理,也没能跑满单个cpu,足见异步编程的高效性。
最后,免费的代理是真的不好用。。。
- Python异步编程与aiohttp检测代理池
- python的异步IO(asyncio aiohttp)
- python aiohttp
- 异步爬虫: async/await 与 aiohttp的使用,以及例子
- python install aiohttp
- python学习——aiohttp
- JWT Authorization in Python aiohttp
- python aiohttp简易使用教程
- aiohttp
- aiohttp
- aiohttp
- 使用 aiohttp 和 asyncio 进行异步请求
- asyncio/aiohttp 异步爬取douban
- wininet异步代理的编程
- Python aiohttp后台导出excel文件
- python的异步编程
- iOS网络编程:五、Post代理异步
- javascript 与 异步编程
- WebService(腾讯QQ在线状态 WEB 服务)
- java综合日期时间处理工具类
- ICP点云配准
- RN————Navigator 报错
- 常见端口漏洞
- Python异步编程与aiohttp检测代理池
- Caused by: java.lang.ClassNotFoundException: net.sf.ehcache.CacheManager
- 相邻两数最大差值
- apache+wsgi+flask配置
- OpenGL-绘制旋转立方体
- Java——this关键字总结
- 面试题:用三种方法模拟实现strlen函数
- loadrunner Web_类函数之web_add_auto_filter()
- JDBC 实效数据分页查询