grequests是如何实现异步的?

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请求并将所有响应发送到单个回调函数。这准确吗?


1
不确定,但您是否可以使用内置的urllib模块,并在后台线程中使用thread模块运行它? - Aya
2
我想我可能必须这样做。我只是感到困惑,并希望验证预期行为。 - cacois
当然。我只是倾向于尽可能使用内置函数来最大化可移植性。 - Aya
3个回答

59

.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() 调用。


2
@martijn pieters,您能否提供一个使用POST方法的示例呢?我无法通过POST调用使其正常工作。 - Avinash
@Avinash:只需使用grequests.post()即可吗?就grequests而言,各种请求方法之间没有区别。 - Martijn Pieters
1
.pool() 实际上是做什么的?我没有理解文档。 - user3347814

11

如果您不想使用grequests,您可以使用标准库中的requeststhreading模块实现带有回调函数的请求。实际上非常容易,而且如果您只想发送带有回调函数的请求,则API比grequests提供的API更加友好。

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)

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 就会被打印出来

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接