使用Python请求进行异步请求

230 投票
13 回答
572274 浏览
提问于 2025-04-17 12:11

我试着使用了Python的requests库文档中提供的示例。

通过使用async.map(rs),我能得到响应的状态码,但我想要获取每个请求页面的内容。不过,下面这个例子是无法工作的:

out = async.map(rs)
print out[0].content

13 个回答

60

我测试了两个库,分别是 requests-futuresgrequests。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 来异步处理多个任务,你需要:

  1. 定义一个函数,说明你想对每个对象做什么(也就是你的任务)
  2. 把这个函数作为事件钩子添加到你的请求中
  3. 在所有请求/操作的列表上调用 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)

撰写回答