最近正在学习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. Hibernate中session回话的get方法和load方法的区别

    1.报错方式不同: 前提:获取的数据不存在 get方法会报异常:空指针异常 load方法会报异常:对象为找到异常,给定值没有行存在. 2.load方法 这种方式总是会返回一个代理而不是真正得去查询数据 ...

  2. C#高级编程五十八天----并行集合

    并行集合 对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行訪问.常常要做的就是对一些队列进行加锁-解锁,然后运行类似插入,删除等等相互排斥操作. .NET4提供了一些封装好的支持并行操作 ...

  3. 8.boost_array_any

    #include <iostream> #include <string> #include <boost/array.hpp> //异构的容器 #include ...

  4. Codeforces 930A. Peculiar apple-tree (dfs)

    题目: 代码: #include <bits\stdc++.h> using namespace std; ]; //b[i]表示距离1号花絮i步的花絮的个数 map <int, l ...

  5. BootStrap学习(三)——重写首页之导航栏和轮播图

    1.按钮 1)帮助文档:http://v3.bootcss.com/css/#buttons 2).btn-lg..btn-sm..btn-xs可以设置按钮的不同尺寸 3).active类设置按钮的激 ...

  6. Sublimi Text3 下Emmet使用技巧

    Emmet真的好用,可以少写很多代码. 初始化文档 HTML文档需要包含一些固定的标签,比如<html>.<head>.<body>等,现在你只需要1秒钟就可以输入 ...

  7. pthread_join/pthread_exit的用法解析

    官方说法: 函数pthread_join用来等待一个线程的结束.函数原型为: extern int pthread_join __P ((pthread_t __th, void **__thread ...

  8. SpringCloud学习笔记(16)----Spring Cloud Netflix之Hystrix Dashboard+Turbine集群监控

    前言: 上一节中,我们使用Hystrix Dashboard,只能看到单个应用内的服务信息.在生产环境中,我们经常是集群状态,所以我们需要用到Turbine这一应用. 作用:汇总系统内的多个服务的数据 ...

  9. SpringCloud学习笔记(11)----Spring Cloud Netflix之Hystrix断路器的使用

    为什么会有断路器? 在微服务架构中,系 是拆分成 一个的服务单元各间通过注册与发现 的方式互相依 赖.每个单元都在不同的进程中运行, 都是通过远程调用的方式进行信 ,这样就有可能因为网络原或 是依赖服 ...

  10. There is no 'root'@'%' registered解决

      把别人机器上的MYSQL中的一个数据库导出来,生成了一个.sql的文件   在我的机器上导入这个.sql文件之后,在数据库连接时出现了如下错误:   “There is no 'root'@'%' ...