最近正在学习Python中的异步编程,看了一些博客后做了一些小测验:对比asyncio+aiohttp的爬虫和asyncio+aiohttp+concurrent.futures(线程池/进程池)在效率中的差异,注释:在爬虫中我几乎没有使用任何计算性任务,为了探测异步的性能,全部都只是做了网络IO请求,就是说aiohttp把网页get完就程序就done了。

结果发现前者的效率比后者还要高。我询问了另外一位博主,(提供代码的博主没回我信息),他说使用concurrent.futures的话因为我全部都是IO任务,如果把这些IO任务分散到线程池/进程池,反而多线程/多进程之间的切换开销还会降低爬虫的效率。我想了想的确如此。

那么我的问题是:仅仅在爬取网页的过程中,就是request.get部分,多线程肯定是没有存在的必要了,因为GIL这个大坑,进程池可能好点,但是性能还是不如异步爬虫,而且更加浪费资源。既然这样,是不是以后在爬虫的爬取网页阶段我们完全都可以用兴起的asyncio+aiohttp代替。(以及其他IO任务比如数据库/文件读写)

当然在数据处理阶段还是要采用多进程,但是我觉得多线程是彻底没用了,原本它相比多进程的优势在于IO型任务,现看来在它的优势完全被异步取代了。(当然问题建立在不考虑兼容2.x)

注:还有一个额外的问题就是,看到一些博客说requests库不支持异步编程是什么意思,为了充分发回异步的优势应该使用aiohttp,我没有看过requests的源代码,但是一些结果显示aiohttp的性能确实更好,各位网友能解释一下吗?

代码

asyncio+aiohttp

import aiohttp

async def fetch_async(a):
async with aiohttp.request('GET', URL.format(a)) as r:
data = await r.json()
return data['args']['a'] start = time.time()
event_loop = asyncio.get_event_loop()
tasks = [fetch_async(num) for num in NUMBERS]
results = event_loop.run_until_complete(asyncio.gather(*tasks)) for num, result in zip(NUMBERS, results):
print('fetch({}) = {}'.format(num, result))

asyncio+aiohttp+线程池比上面要慢1秒

async def fetch_async(a):
async with aiohttp.request('GET', URL.format(a)) as r:
data = await r.json()
return a, data['args']['a'] def sub_loop(numbers):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
tasks = [fetch_async(num) for num in numbers]
results = loop.run_until_complete(asyncio.gather(*tasks))
for num, result in results:
print('fetch({}) = {}'.format(num, result)) async def run(executor, numbers):
await asyncio.get_event_loop().run_in_executor(executor, sub_loop, numbers) def chunks(l, size):
n = math.ceil(len(l) / size)
for i in range(0, len(l), n):
yield l[i:i + n] event_loop = asyncio.get_event_loop()
tasks = [run(executor, chunked) for chunked in chunks(NUMBERS, 3)]
results = event_loop.run_until_complete(asyncio.gather(*tasks)) print('Use asyncio+aiohttp+ThreadPoolExecutor cost: {}'.format(time.time() - start))

传统的requests + ThreadPoolExecutor比上面慢了3倍

import time
import requests
from concurrent.futures import ThreadPoolExecutor NUMBERS = range(12)
URL = 'http://httpbin.org/get?a={}' def fetch(a):
r = requests.get(URL.format(a))
return r.json()['args']['a'] start = time.time()
with ThreadPoolExecutor(max_workers=3) as executor:
for num, result in zip(NUMBERS, executor.map(fetch, NUMBERS)):
print('fetch({}) = {}'.format(num, result)) print('Use requests+ThreadPoolExecutor cost: {}'.format(time.time() - start))

补充

以上问题建立在CPython,至于我喜欢用多线程,不喜欢协程风格这类型的回答显然不属于本题讨论范畴。我主要想请教的是:
如果Python拿不下GIL,我认为未来理想的模型应该是多进程 + 协程(asyncio+aiohttp)。uvloopsanic以及500lines一个爬虫项目已经开始这么干了。不讨论兼容型问题,上面的看法是否正确,有一些什么场景协程无法取代多线程。

异步有很多方案,twisted, tornado等都有自己的解决方案,问题建立在asyncio+aiohttp的协程异步。

还有一个问题也想向各位网友请教一下

Python有了asyncio和aiohttp在爬虫这类型IO任务中多线程/多进程还有存在的必要吗? >> node.js

这个答案描述的挺清楚的:
http://www.goodpm.net/postreply/node.js/1010000007987098/Python有了asyncio和aiohttp在爬虫这类型IO任务中多线程多进程还有存在的必要吗.html

Python有了asyncio和aiohttp在爬虫这类型IO任务中多线程/多进程还有存在的必要吗?的更多相关文章

  1. python链家网高并发异步爬虫asyncio+aiohttp+aiomysql异步存入数据

    python链家网二手房异步IO爬虫,使用asyncio.aiohttp和aiomysql 很多小伙伴初学python时都会学习到爬虫,刚入门时会使用requests.urllib这些同步的库进行单线 ...

  2. 异步:asyncio和aiohttp的一些应用(1)

    1. asyncio 1.1asyncio/await 用法 async/await 是 python3.5中新加入的特性, 将异步从原来的yield 写法中解放出来,变得更加直观. 在3.5之前,如 ...

  3. python链家网高并发异步爬虫and异步存入数据

    python链家网二手房异步IO爬虫,使用asyncio.aiohttp和aiomysql 很多小伙伴初学python时都会学习到爬虫,刚入门时会使用requests.urllib这些同步的库进行单线 ...

  4. 一个使用 asyncio 开发的网络爬虫(译文)

    原文地址:https://www.aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html 作者简介 A. Jesse Jiryu ...

  5. 使用Python自动填写问卷星(pyppeteer反爬虫版)

    写此文的目的是为了方便寒假自己忘记填问卷星 一开始的想法和去年一样,去年就写过一版,想着今年不过就是改改数据,换换id而已,另外没想到的事情发生了... 满怀信心的写完代码 from selenium ...

  6. PHP, Python, Node.js 哪个比较适合写爬虫?

    PHP, Python, Node.js 哪个比较适合写爬虫? 1.对页面的解析能力2.对数据库的操作能力(mysql)3.爬取效率4.代码量推荐语言时说明所需类库或者框架,谢谢.比如:python+ ...

  7. python scrapy 入门,10分钟完成一个爬虫

    在TensorFlow热起来之前,很多人学习python的原因是因为想写爬虫.的确,有着丰富第三方库的python很适合干这种工作. Scrapy是一个易学易用的爬虫框架,尽管因为互联网多变的复杂性仍 ...

  8. Python十分适合用来开发网页爬虫

    Python十分适合用来开发网页爬虫,理由如下:1.抓取网页自身的接口比较与其他静态编程语言,如java,c#,c++,python抓取网页文档的接口更简练:比较其他动态脚本语言,如perl,shel ...

  9. 【译】深入理解python3.4中Asyncio库与Node.js的异步IO机制

    转载自http://xidui.github.io/2015/10/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3python3-4-Asyncio%E5%BA%93% ...

随机推荐

  1. leveldb学习:sstable(2)

    block写入:block_builder block.h和.cc里定义了block的entry存储格式和restart,提供了entry的查找接口以及迭代器.那么怎样往写block里写entry呢? ...

  2. 从头认识java-15.3 使用HashSet须要注意的地方

    这一章节我们来讨论一下使用Set的各种实现须要注意的地方. Set接口的经常使用实现类有:HashSet.TreeSet,LinkedHashSet 1.HashSet 大家对于HashSet的印象都 ...

  3. C++语言笔记系列之十八——虚函数(1)

    1.C++中的多态 (1)多态性:同一个函数的调用能够进行不同的操作,函数重载是实现多态的一种手段. (2)联编:在编译阶段进行联接.即是在编译阶段将一个函数的调用点和函数的定义点联接起来. A.静态 ...

  4. m_Orchestrate learning system---十六、如何快速在一堆字符图标中找到所需

    m_Orchestrate learning system---十六.如何快速在一堆字符图标中找到所需 一.总结 一句话总结:find查找字符 比如说找teacher feedback 的图标,可以多 ...

  5. kafka 0.11 spark 2.11 streaming例子

    """ Counts words in UTF8 encoded, '\n' delimited text received from the network every ...

  6. JavaScript学习——JS对象和全局函数

    1. Array对象 数组的特点:长度可变!数组的长度=最大角标+1 2.Boolean对象 如果value 不写,那么默认创建的结果为false 3.Date对象 getTime()返回1970年1 ...

  7. [转]C++ 获取文件夹下的所有文件名

    转自http://www.cnblogs.com/fnlingnzb-learner/p/6424563.html 头文件:#include<io.h> char * filePath = ...

  8. NodeJS学习笔记 (2)文件系统操作-fs(ok)

    原文:https://github.com/chyingp/nodejs-learning-guide/blob/master/%E6%A8%A1%E5%9D%97/fs.md#%E9%80%9A%E ...

  9. LightOJ-1220 Mysterious Bacteria 唯一分解定理 带条件的最大公因数

    题目链接:https://cn.vjudge.net/problem/LightOJ-1220 题意 给x=y^p,问p最大多少 注意x可能负数 思路 唯一分解定理,求各素因数指数的GCD 注意负数的 ...

  10. GIT配置多用户

    在公司工作的时候有时候想提交一点代码到github上,然后一台电脑上就需要配置两个账号分别访问github和公司的gitlab 1. 分别生成两个key 为什么要生成两个key的原因我也不清楚,望路过 ...