43

我一直在使用 python requests 库,最近需要异步发出请求,这意味着我想发送 HTTP 请求,让我的主线程继续执行,并在调用时调用回调请求返回。

自然,我被引导到了 grequests 库(https://github.com/kennethreitz/grequests),但我对这种行为感到困惑。例如:

import grequests

def print_res(res):
    from pprint import pprint
    pprint (vars(res))

req = grequests.get('http://www.codehenge.net/blog', hooks=dict(response=print_res))
res = grequests.map([req])

for i in range(10):
    print i

上面的代码将产生以下输出:

<...large HTTP response output...>

0
1
2
3
4
5
6
7
8
9

grequests.map() 调用显然会阻塞,直到 HTTP 响应可用。似乎我在这里误解了“异步”行为,而 grequests 库仅用于同时执行多个 HTTP 请求并将所有响应发送到单个回调。这是准确的吗?

4

3 回答 3

57

.map()旨在并行运行多个 URL 的检索,并且确实会等待这些任务完成(gevent.joinall(jobs)调用)。

.send()改为使用实例Pool生成作业:

req = grequests.get('http://www.codehenge.net/blog', hooks=dict(response=print_res))
job = grequests.send(req, grequests.Pool(1))

for i in range(10):
    print i

如果没有池,该.send()调用仍将阻塞,但仅限于gevent.spawn()它执行的调用。

于 2013-04-15T13:38:05.813 回答
11

如果您不想使用grequests,您可以使用requests+threading标准库中的模块来实现带有回调的请求。这实际上非常简单,如果您只想发送带有回调的请求,那么 API 比grequests.

from threading import Thread

from requests import get, post, put, patch, delete, options, head



request_methods = {
    'get': get,
    'post': post,
    'put': put,
    'patch': patch,
    'delete': delete,
    'options': options,
    'head': head,
}


def async_request(method, *args, callback=None, timeout=15, **kwargs):
    """Makes request on a different thread, and optionally passes response to a
    `callback` function when request returns.
    """
    method = request_methods[method.lower()]
    if callback:
        def callback_with_args(response, *args, **kwargs):
            callback(response)
        kwargs['hooks'] = {'response': callback_with_args}
    kwargs['timeout'] = timeout
    thread = Thread(target=method, args=args, kwargs=kwargs)
    thread.start()

您可以验证它是否像 JS 中的 AJAX 调用一样工作:您在另一个线程上发送请求,在主线程上做一些事情,当请求返回时您调用回调。这个回调只是打印出响应内容。

async_request('get', 'http://httpbin.org/anything', callback=lambda r: print(r.json()))
for i in range(10):
    print(i)
于 2017-07-05T04:26:41.420 回答
1

创建一个请求列表,然后发送它们.imap

event_list = [grequests.get(url_viol_card, params={"viol": i},
              session=session) for i in print_ev_list]
for r in grequests.imap(event_list, size=5):
    print(r.request.url)
  • sessionrequests.Session()对象(可选)
  • size=5同时发送 5 个请求:一旦完成其中一个请求,就会发送下一个请求
  • 在这个例子中,当任何请求(无序)完成时,它的 url 就会被打印出来
于 2017-02-15T06:32:18.690 回答