Подтвердить что ты не робот

Каким образом grequests асинхронны?

Я использую библиотеку запросов python в течение некоторого времени, и недавно мне нужно было сделать запрос асинхронно, то есть я хотел бы отправить запрос 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-запросов и отправки всех ответов на один обратный вызов. Это точно?

4b9b3361

Ответ 1

.map() предназначен для параллельного запуска извлечения нескольких URL-адресов и действительно будет ожидать завершения этих задач (gevent.joinall(jobs))).

.send() этого используйте .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

Если вы не хотите использовать grequests, вы можете просто реализовать запросы с обратными вызовами, используя модуль requests + the 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()

Вы можете проверить, что он работает как вызовы AJAX в JS: вы отправляете запрос в другой поток, делаете некоторые вещи в основном потоке, и когда запрос возвращает вы вызываете обратный вызов. Этот обратный вызов просто распечатывает содержимое ответа.

async_request('get', 'http://httpbin.org/anything', callback=lambda r: print(r.json()))
for i in range(10):
    print(i)

Ответ 3

Создайте список запросов, а затем отправьте их с помощью .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)
  • session - объект requests.Session() (необязательно)
  • size=5 отправлять 5 запросов одновременно: как только одна из них будет завершена, следующий отправляется
  • В этом примере, когда какой-либо запрос (неупорядоченный) завершен, его URL-адрес печатается