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?
.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.
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)
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