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

2017-11-02 09:43:51 +08:00
 NxnXgpuPSfsIT

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,你不会后悔的!

6922 次点击
所在节点    Python
38 条回复
tikazyq
2017-11-02 15:40:29 +08:00
太给力了!给作者点个赞
NxnXgpuPSfsIT
2017-11-02 17:44:17 +08:00
@timonwong 是的,这是有研究过的。
wjm2038
2017-11-03 01:29:00 +08:00
qsnow6
2017-11-03 09:53:24 +08:00
great
qsnow6
2017-11-03 09:54:54 +08:00
看起来学习成本很低啊;刚好在找 aiohttp 的低成本解决方案,aiohttp 学习成本太高了
zhengxiaowai
2017-11-03 10:30:01 +08:00
先 star 一下,有空看看源码学习一下。

----

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

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

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

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

区别的话,最显而易见的,你有没有发现 adapter 里面完全就不是一个东西了?
对于协程和非协程有区别的 api,比如 iter_content 也完全不一样了。
另外,就是把 send 方法改成 Tornado 版本,其实也不是特别简单的一件事情啦。
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
2017-11-03 11:59:27 +08:00
@zhengxiaowai
其实我也就是看了些 Requests 和 Tornado 源码的水平,看源码有问题的话可以 qq 咱们多交流。
加个群吧我来加你,462703741,写的不行的地方发现了就一点一点改。
NxnXgpuPSfsIT
2017-11-03 12:00:22 +08:00
@fxxkgw 你更新一下版本呀,python -m pip install requests -U
bluesky139
2017-11-03 13:27:03 +08:00
大概看了一眼,实际上是把 requests 里的参数取出来再用 tornado 的 http 进行请求?
NxnXgpuPSfsIT
2017-11-03 13:34:21 +08:00
@bluesky139 tornado 拆到 TCPClient 这层
guyskk0x0
2017-11-03 18:06:51 +08:00
上周也开源了一个 Curio + Requests: Async HTTP for Humans https://www.v2ex.com/t/401739
哈哈
NxnXgpuPSfsIT
2017-11-03 18:59:28 +08:00
@guyskk0x0 哈哈,其实 Curequests 就是我文章开头说到的同类项目,不是你写完了,我的项目再过半年我也抽不出足够时间来写完文档。倒是提醒我了,下次提交我把你的友链加上去!
guyskk0x0
2017-11-03 19:10:33 +08:00
@NxnXgpuPSfsIT #35 共勉,我也加个友链!
Marsss
2017-11-07 09:18:18 +08:00
实际写爬虫的时候,同一个 ip,这样的速度,是会被封锁的,使用代理的话,一般代理商能提供的 ip 切换速度是有限的,基本跟不上这种速度。或许大量静态代理能用得上这种协程。不知道我理解的对不对。
wangycc
2017-12-29 20:49:15 +08:00
我司两个后台在评论里面

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/402829

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX