使用Python请求进行异步请求
我试着使用了Python的requests库文档中提供的示例。
通过使用async.map(rs)
,我能得到响应的状态码,但我想要获取每个请求页面的内容。不过,下面这个例子是无法工作的:
out = async.map(rs)
print out[0].content
13 个回答
60
我测试了两个库,分别是 requests-futures 和 grequests。grequests 的速度更快,但它需要进行一些复杂的修改(叫做 monkey patching),而且还会带来额外的依赖问题。相比之下,requests-futures 的速度比 grequests 慢好几倍。于是我决定自己写一个,把 requests 包装到 ThreadPoolExecutor 里,这样的速度几乎和 grequests 一样快,而且没有外部依赖。
import requests
import concurrent.futures
def get_urls():
return ["url1","url2"]
def load_url(url, timeout):
return requests.get(url, timeout = timeout)
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
future_to_url = {executor.submit(load_url, url, 10): url for url in get_urls()}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
resp_err = resp_err + 1
else:
resp_ok = resp_ok + 1
110
async
现在是一个独立的模块,叫做 grequests
。
你可以在这里查看:https://github.com/spyoungtech/grequests
还有这里:在Python中发送多个HTTP请求的理想方法是什么?
安装:
$ pip install grequests
使用:
构建一个请求堆栈:
import grequests
urls = [
'http://www.heroku.com',
'http://tablib.org',
'http://httpbin.org',
'http://python-requests.org',
'http://kennethreitz.com'
]
rs = (grequests.get(u) for u in urls)
发送这个堆栈
grequests.map(rs)
结果看起来像这样
[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]
grequests 似乎没有对同时发送的请求数量设置限制,也就是说,当向同一个服务器发送多个请求时,它不会限制你。
193
注意
下面的回答对版本 v0.13.0 及以上的 requests 不适用。因为在这个问题提出后,异步功能已经转移到了 grequests。不过,你只需要把下面的 requests
替换成 grequests
,就可以正常工作了。
我保留这个回答是为了反映原始问题,那个问题是关于使用版本小于 v0.13.0 的 requests。
要用 async.map
来异步处理多个任务,你需要:
- 定义一个函数,说明你想对每个对象做什么(也就是你的任务)
- 把这个函数作为事件钩子添加到你的请求中
- 在所有请求/操作的列表上调用
async.map
示例:
from requests import async
# If using requests > v0.13.0, use
# from grequests import async
urls = [
'http://python-requests.org',
'http://httpbin.org',
'http://python-guide.org',
'http://kennethreitz.com'
]
# A simple task to do to each response object
def do_something(response):
print response.url
# A list to hold our things to do via async
async_list = []
for u in urls:
# The "hooks = {..." part is where you define what you want to do
#
# Note the lack of parentheses following do_something, this is
# because the response will be used as the first argument automatically
action_item = async.get(u, hooks = {'response' : do_something})
# Add the task to our list of things to do via async
async_list.append(action_item)
# Do our list of things to do via async
async.map(async_list)