Trip: 给 Requests 加上协程,一百份网络请求一份时间 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
NxnXgpuPSfsIT
V2EX    Python

Trip: 给 Requests 加上协程,一百份网络请求一份时间

  •  3
     
  •   NxnXgpuPSfsIT
    littlecodersh 2017-11-02 09:43:51 +08:00 7301 次点击
    这是一个创建于 2901 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Trip 是一个协程的网络库,如 Requests 一般简单的操作,程序不再被网络阻塞。

    兼容 Python 2.7+ 的所有版本,主流三大操作系统。

    基于两大依赖包:TRIP: Tornado & Requests In Pair

    感谢 Tornado 与 Requests 让想法可以快速变成现实,坦诚的说,这个项目我只做了一些简单的工作。

    其实上半年就写好了这个项目,结果文档拖到了今天才大致写了一些,不得不感谢一下同类项目对我的督促。

    让协程变的简单

    这是一个让协程变的简单的项目,你只需要这样:

    import trip @trip.coroutine def main(): r = yield trip.get('https://httpbin.org/get', auth=('user', 'pass')) print(r.content) trip.run(main) 

    一百份请求一份时间

    基于 Tornado 的协程让网络阻塞不再成为问题:(这里演示兼容用法)

    import time, functools import requests, trip def timeit(fn): start_time = time.time() fn() return time.time() - start_time url = 'http://httpbin.org/get' times = 10 # 100 changed for inland network delay def fetch(): r = [requests.get(url) for i in range(times)] return r @trip.coroutine def async_fetch(): r = yield [trip.get(url) for i in range(times)] raise trip.Return(r) print('Non-trip cost: %ss' % timeit(fetch)) print('Trip cost: %ss' % timeit(functools.partial(trip.run, async_fetch))) # Result: # Non-trip cost: 17.90799999237s # Trip cost: 0.172300004959s 

    由于协程的特性,所有的等待时间重合在了一起。

    你不需要每个请求开一个线程,主线程中一切也可以井然有序的进行。

    可以想象如果你在写一个爬虫,这将节省多少时间!

    让协程服务人类

    基于 Requests 的操作方式让协程 HTTP 从未如此简单:(这里使用 Python3 演示 async/await )

    >>> async def main(): ... global r ... r = await trip.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass')) ... >>> trip.run(main) >>> r.status_code 200 >>> r.headers['content-type'] 'application/json' >>> r.encoding None >>> r.text u'{"authenticated": true,...' >>> r.json() {u'authenticated': True, u'user': u'user'} 

    只要你有一些 Requests 基础就可以轻松使用 Trip,协程不再遥不可及。

    重现了几乎所有 Requests 的操作,最大限度的减少了你的学习成本。

    以一个爬虫为例

    为了不打扰正常网站的运行,这里以httpbin.org作为目标网站。 设置 Cookies 模拟登陆,get 请求模拟爬取内容。

    那么普通 Requests 是这样的:

    import requests url = 'http://httpbin.org' s = requests.Session() def fetch(times=10): s.get('%s/cookies/set?name=value' % url) r = [s.get('%s/get' % url) for i in range(times)] print r fetch() 

    使用 Trip 以后就会变成这样:

    import trip url = 'http://httpbin.org' s = trip.Session() @trip.coroutine def fetch(times=10): yield s.get('%s/cookies/set?name=value' % url) r = yield [s.get('%s/get' % url) for i in range(times)] print r trip.run(fetch) 

    几乎不需要修改代码,爬虫就获得了协程的特性!

    最后

    爬虫耗时太久优化困难吗? 各种协程网络框架难以使用吗? 大型爬虫框架臃肿无法灵活定制吗?

    试试 Trip,你不会后悔的!

    38 条回复    2017-12-29 20:49:15 +08:00
    Chyroc
        1
    Chyroc  
       2017-11-02 10:09:39 +08:00
    厉害
    golmic
        2
    golmic  
       2017-11-02 10:38:31 +08:00
    wwqgtxx
        3
    wwqgtxx  
       2017-11-02 10:40:28 +08:00 via iPhone
    mark
    jyf
        4
    jyf  
       2017-11-02 10:46:20 +08:00
    跟 grequests 比如何呢
    CSM
        5
    CSM  
       2017-11-02 10:59:15 +08:00 via Android
    跟 aiohttp 相比有什么优势?
    leavic
        6
    leavic  
       2017-11-02 11:01:32 +08:00
    noli
        7
    noli  
       2017-11-02 11:05:59 +08:00
    我是不是唯一一个想起 gevent 的 ?
    NxnXgpuPSfsIT
        8
    NxnXgpuPSfsIT  
    OP
       2017-11-02 11:41:50 +08:00
    @jyf @noli gevent 就我的体验,感受最深的就是总有一些根本意想不到的问题因为 patch 出现。
    我很难定位问题,有的还是完全无法修复问题。
    NxnXgpuPSfsIT
        9
    NxnXgpuPSfsIT  
    OP
       2017-11-02 11:48:24 +08:00
    @CSM 兼容更多版本,学习成本更少
    est
        10
    est  
       2017-11-02 11:49:14 +08:00
    居然最后没有广告链接。
    NxnXgpuPSfsIT
        11
    NxnXgpuPSfsIT  
    OP
       2017-11-02 11:55:49 +08:00
    @est 毕竟自己随便写写,有广告位也没有广告发
    noli
        12
    noli  
       2017-11-02 11:57:13 +08:00
    @NxnXgpuPSfsIT

    是有遇到过,但写爬虫的时候很难遇到。
    确实也很致命,在 python 的范围内很难修复。

    所以现在不用 python 写爬虫 ;P
    jyf
        13
    jyf  
       2017-11-02 12:25:04 +08:00
    @NxnXgpuPSfsIT 我只是想了解下性能方面的比较数据 :D
    cheesea
        14
    cheesea  
       2017-11-02 13:08:23 +08:00
    据说 aiohttp 对 http 的解析拖慢了它的速度,不知道这个怎么样?
    janxin
        15
    janxin  
       2017-11-02 13:10:12 +08:00
    @cheesea 那个是 2.0 以前的事了,之后重写优化过
    mdzz
        16
    mdzz  
       2017-11-02 13:17:24 +08:00
    想起之前的一个帖子 /t/401575
    timonwong
        17
    timonwong  
       2017-11-02 13:39:57 +08:00   1
    给用户提个醒,resp.text 以及 resp.json 相关,如果 Content-Type 没有 charset,比如一个裸的 application/json,会导致 chardet 执行,chardet 执行速度比较慢(其实可以说很慢,比如一个接口一个 roundtrip 不到 1ms,跑个 chardet 都要 4-5 毫秒,就不可接受了),因此会降低整体性能(当然这个问题在 requests 里面也存在)。
    ideascf
        18
    ideascf  
       2017-11-02 13:40:22 +08:00
    还是 gevent 来得爽,真正的无侵入支持协程
    wsxka
        19
    wsxka  
       2017-11-02 14:29:14 +08:00
    666 !
    takanasi
        20
    takanasi  
       2017-11-02 15:21:17 +08:00
    看到兼容 3.7 还以为 3.7 出了
    tikazyq
        21
    tikazyq  
       2017-11-02 15:40:29 +08:00
    太给力了!给作者点个赞
    NxnXgpuPSfsIT
        22
    NxnXgpuPSfsIT  
    OP
       2017-11-02 17:44:17 +08:00
    @timonwong 是的,这是有研究过的。
    wjm2038
        23
    wjm2038  
       2017-11-03 01:29:00 +08:00 via Android
    qsnow6
        24
    qsnow6  
       2017-11-03 09:53:24 +08:00
    great
    qsnow6
        25
    qsnow6  
       2017-11-03 09:54:54 +08:00
    看起来学习成本很低啊;刚好在找 aiohttp 的低成本解决方案,aiohttp 学习成本太高了
    zhengxiaowai
        26
    zhengxiaowai  
       2017-11-03 10:30:01 +08:00
    先 star 一下,有空看看源码学习一下。

    ----

    简单看了一下,trip 基于 Tornado 的封装。所以充斥的大量 Tronado 类似的语法。

    Tronado 最为人诟病之一就是语法比较恶心, 不够 pythonic...

    我觉得可以把这些恶心的语法封装成 API 的形式,比如 raise Return() 这种。
    zhengxiaowai
        27
    zhengxiaowai  
       2017-11-03 10:48:39 +08:00
    我去,大兄弟。。我就说看源码怎么那么熟悉呢。。基本都是 requests 的 copy 啊。基本就把 send 方法改成了 Tornado 版本的啦
    NxnXgpuPSfsIT
        28
    NxnXgpuPSfsIT  
    OP
       2017-11-03 11:49:10 +08:00
    @zhengxiaowai
    说到的“把这些恶心的语法封装成 API 的形式,比如 raise Return() 这种”具体是指什么,
    我没太理解你的意思,可以具体和我解释一下吗?

    外部 api 遵从 Requests 就是功能之一呀,但这不是 copy。
    我把不需要更改不必要的内容都用了 import,留下来的肯定都是必要的、文档或是必须重新整合修改的。

    区别的话,最显而易见的,你有没有发现 adapter 里面完全就不是一个东西了?
    对于协程和非协程有区别的 api,比如 iter_content 也完全不一样了。
    另外,就是把 send 方法改成 Tornado 版本,其实也不是特别简单的一件事情啦。
    fxxkgw
        29
    fxxkgw  
       2017-11-03 11:51:52 +08:00
    = = 报错,requests 版本兼容有问题。。。

    >>> import trip
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/usr/local/lib/python2.7/site-packages/trip/__init__.py", line 12, in <module>
    from .api import (
    File "/usr/local/lib/python2.7/site-packages/trip/api.py", line 10, in <module>
    from . import sessions
    File "/usr/local/lib/python2.7/site-packages/trip/sessions.py", line 21, in <module>
    from requests.sessions import (
    ImportError: cannot import name preferred_clock
    NxnXgpuPSfsIT
        30
    NxnXgpuPSfsIT  
    OP
       2017-11-03 11:59:27 +08:00
    @zhengxiaowai
    其实我也就是看了些 Requests 和 Tornado 源码的水平,看源码有问题的话可以 qq 咱们多交流。
    加个群吧我来加你,462703741,写的不行的地方发现了就一点一点改。
    NxnXgpuPSfsIT
        31
    NxnXgpuPSfsIT  
    OP
       2017-11-03 12:00:22 +08:00
    @fxxkgw 你更新一下版本呀,python -m pip install requests -U
    bluesky139
        32
    bluesky139  
       2017-11-03 13:27:03 +08:00 via Android
    大概看了一眼,实际上是把 requests 里的参数取出来再用 tornado 的 http 进行请求?
    NxnXgpuPSfsIT
        33
    NxnXgpuPSfsIT  
    OP
       2017-11-03 13:34:21 +08:00
    @bluesky139 tornado 拆到 TCPClient 这层
    guyskk0x0
        34
    guyskk0x0  
       2017-11-03 18:06:51 +08:00
    上周也开源了一个 Curio + Requests: Async HTTP for Humans t/401739
    哈哈
    NxnXgpuPSfsIT
        35
    NxnXgpuPSfsIT  
    OP
       2017-11-03 18:59:28 +08:00 via iPhone
    @guyskk0x0 哈哈,其实 Curequests 就是我文章开头说到的同类项目,不是你写完了,我的项目再过半年我也抽不出足够时间来写完文档。倒是提醒我了,下次提交我把你的友链加上去!
    guyskk0x0
        36
    guyskk0x0  
       2017-11-03 19:10:33 +08:00
    @NxnXgpuPSfsIT #35 共勉,我也加个友链!
    Marsss
        37
    Marsss  
       2017-11-07 09:18:18 +08:00
    实际写爬虫的时候,同一个 ip,这样的速度,是会被封锁的,使用代理的话,一般代理商能提供的 ip 切换速度是有限的,基本跟不上这种速度。或许大量静态代理能用得上这种协程。不知道我理解的对不对。
    wangycc
        38
    wangycc  
       2017-12-29 20:49:15 +08:00
    我司两个后台在评论里面
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2414 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 15:42 PVG 23:42 LAX 08:42 JFK 11:42
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86