web-dev-qa-db-fra.com

De quelle manière les grequests sont-ils asynchrones?

J'utilise la bibliothèque de demandes python depuis un certain temps et j'ai récemment eu besoin de faire une demande de manière asynchrone, ce qui signifie que je voudrais envoyer la demande HTTP, que mon thread principal continue à exécuter et avoir un rappel appelé lorsque la demande revient.

Naturellement, j'ai été conduit à la bibliothèque grequests ( https://github.com/kennethreitz/grequests ), mais je suis confus quant au comportement. Par exemple:

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

Le code ci-dessus produira la sortie suivante:

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

0
1
2
3
4
5
6
7
8
9

L'appel grequests.map () bloque évidemment jusqu'à ce que la réponse HTTP soit disponible. Il semble probable que j'ai mal compris le comportement "asynchrone" ici, et la bibliothèque grequests sert uniquement à effectuer plusieurs requêtes HTTP simultanément et à envoyer toutes les réponses à un seul rappel. Est-ce exact?

38
cacois

.map() est destiné à exécuter la récupération de plusieurs URL en parallèle, et attendra en effet que ces tâches se terminent (gevent.joinall(jobs)) est appelée).

Utilisez .send() à la place pour générer des jobs, en utilisant une instance 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

Sans le pool, l'appel .send() se bloquera toujours, mais uniquement pour l'appel gevent.spawn() qu'il exécute.

51
Martijn Pieters

Si vous ne voulez pas utiliser grequests, vous pouvez simplement implémenter des requêtes avec des rappels en utilisant requests + le module threading de la bibliothèque standard. C'est vraiment très simple, et si tout ce que vous voulez faire est d'envoyer des demandes avec des rappels, l'API est plus agréable que celle fournie par 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()

Vous pouvez vérifier qu'il fonctionne comme un appel AJAX dans JS: vous envoyez une demande sur un autre thread, faites des choses sur le thread principal et lorsque la demande revient, vous appelez un rappel. Ce rappel imprime simplement le contenu de la réponse.

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

Créez une liste de demandes, puis envoyez-les avec .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 est requests.Session() objet (facultatif)
  • size=5 Envoyez 5 demandes simultanément: dès que l'une d'elles est terminée, la suivante est envoyée
  • Dans cet exemple, lorsqu'une demande (non ordonnée) est terminée, son URL est imprimée
0
Winand