代码:
import sys
import gevent
from gevent import monkey
monkey.patch_all()
import requests
import urllib2
def worker(url, use_urllib2=False):
if use_urllib2:
content = urllib2.urlopen(url).read().lower()
else:
content = requests.get(url, prefetch=True).content.lower()
title = content.split('<title>')[1].split('</title>')[0].strip()
urls = ['http://www.mail.ru']*5
def by_requests():
jobs = [gevent.spawn(worker, url) for url in urls]
gevent.joinall(jobs)
def by_urllib2():
jobs = [gevent.spawn(worker, url, True) for url in urls]
gevent.joinall(jobs)
if __name__=='__main__':
from timeit import Timer
t = Timer(stmt="by_requests()", setup="from __main__ import by_requests")
print 'by requests: %s seconds'%t.timeit(number=3)
t = Timer(stmt="by_urllib2()", setup="from __main__ import by_urllib2")
print 'by urllib2: %s seconds'%t.timeit(number=3)
sys.exit(0)
这个结果:
by requests: 18.3397213892 seconds
by urllib2: 2.48605842363 seconds
在嗅探器中它看起来像这样:
说明:前5个请求由请求库发出,接下来的5个请求由urllib2库发送。 红色 - 是工作冻结的时间,黑暗 - 当数据接收... wtf?!
如果套接字库修补并且库必须以相同的方式工作,它是如何实现的? 如何在没有requests.async的情况下使用请求进行异步工作?
答案 0 :(得分:15)
对不起Kenneth Reitz。他的图书馆很棒。
我很蠢。我需要像这样选择针对httplib的猴子补丁:
gevent.monkey.patch_all(httplib=True)
因为默认情况下禁用了httplib的补丁。
答案 1 :(得分:7)
正如Kenneth所指出的,我们可以做的另一件事是让requests
模块处理异步部分。我已相应地更改了您的代码。同样,对我而言,结果始终表明requests
模块的性能优于urllib2
这样做意味着我们无法“线程化”回叫部分。但这应该没问题,因为由于请求/响应延迟,只能通过HTTP请求获得主要收益。
import sys
import gevent
from gevent import monkey
monkey.patch_all()
import requests
from requests import async
import urllib2
def call_back(resp):
content = resp.content
title = content.split('<title>')[1].split('</title>')[0].strip()
return title
def worker(url, use_urllib2=False):
if use_urllib2:
content = urllib2.urlopen(url).read().lower()
title = content.split('<title>')[1].split('</title>')[0].strip()
else:
rs = [async.get(u) for u in url]
resps = async.map(rs)
for resp in resps:
call_back(resp)
urls = ['http://www.mail.ru']*5
def by_requests():
worker(urls)
def by_urllib2():
jobs = [gevent.spawn(worker, url, True) for url in urls]
gevent.joinall(jobs)
if __name__=='__main__':
from timeit import Timer
t = Timer(stmt="by_requests()", setup="from __main__ import by_requests")
print 'by requests: %s seconds'%t.timeit(number=3)
t = Timer(stmt="by_urllib2()", setup="from __main__ import by_urllib2")
print 'by urllib2: %s seconds'%t.timeit(number=3)
sys.exit(0)
这是我的一个结果:
by requests: 2.44117593765 seconds
by urllib2: 4.41298294067 seconds
答案 2 :(得分:5)
请求已将gevent支持集成到代码库中:
http://docs.python-requests.org/en/latest/user/advanced/#asynchronous-requests
答案 3 :(得分:2)
我在我的机器上运行了您的代码(python 2.7.1
,gevent 0.13.0
,requests 0.10.6
)。事实证明,使用请求模块时,时间总是好一两秒。你使用的是什么版本?升级可能只是为您解决问题。
by requests: 3.7847161293 seconds
by urllib2: 4.92611193657 seconds
by requests: 2.90777993202 seconds
by urllib2: 7.99798607826 seconds
答案 4 :(得分:2)
来自请求文档Blocking Or Non-Blocking:
如果您担心使用阻塞IO,那么有很多项目将Requests与Python的异步框架结合起来。两个很好的例子是grequests和requests-futures。