Utiliser requests de manière non bloquante facilement


En attendant le dossier sur la programmation non bloquante, voici une petite lib qui résout un cas d’école : faire une requête HTTP sans bloquer avec une jolie API, en pur Python.

Pour ça, on dégaine pip et installe requests-futures, un plugin pour la célèbre lib requests qui fonctionne avec Python 2 et 3 :

pip install requests-futures

requests-futures va créer pour vous une pool de workers (2 par défaut) et quand vous faites une requête, la lib vous retourne un objet future qui vous permet d’attacher un callback.

Fiou, le nombre de liens référant à d’autres articles du blog est en train d’exploser.

Exemple :

import time
from requests_futures.sessions import FuturesSession
 
# Cette session est notre point d'entrée, c'est elle
# qui gère nos workers. Faites help(FuturesSession)
# pour voir ses paramètres.
session = FuturesSession()
 
# Les URLs sur lesquelles on va faire
# nos requêtes
URLs = [
    "http://sametmax.com",
    "http://sebsauvage.net",
    "http://indexerror.net",
    "http://afpy.org",
    "http://0bin.net"
]
 
# Notre callback qui sera appelé quand une 
# des requêtes sera terminée. Il reçoit
# l'objet future pour seul paramètre
def faire_un_truc_avec_le_resultat(future):
    # On est juste intéressé par le résutlat, qui
    # est un objet response typique de la lib
    # request
    response = future.result()
    print(response.url, response.status_code)
 
# On traite chaque URL. Comme on a 2 workers,
# on pourra traiter au mieux 2 URLs en parallèle,
# mais toujours sans bloquer le programme
# principal
for url in URLs:
    # On fait notre requête GET
    future = session.get(url)
    # On rajoute le callback à appeler quand
    # le résultat de la requête arrive.
    # La flemme de faire la gestion des erreurs.
    future.add_done_callback(faire_un_truc_avec_le_resultat)
 
# Juste pour montrer que c'est bien non bloquant
for x in range(10):
    print(x)
    time.sleep(1)

Output :

0
1
(u'http://sebsauvage.net/', 200)
(u'http://sametmax.com/', 200)
2
(u'http://indexerror.net/', 200)
(u'http://0bin.net/', 200)
(u'http://www.afpy.org/', 200)
3
4
5
6
7
8
9

On remerciera turgon37 pour sa question sur IndexError qui m’a amené à écrire cet article.

12 thoughts on “Utiliser requests de manière non bloquante facilement

  • dineptus

    Il manque un apostrophe dans “d’école”.

    Dans le commentaire avant faire_un_truc_avec_le_resultat(future): c’est le seuL paramètre

    Chaque URL (sans s), à l’inverse en ligne suivante on pourra traiter 2 URLs avec un s (ou pas, si on considère que c’est un nom propre ?)

  • toto

    D’où sortent ces 2 workers ? Est-ce une valeur par défaut ?

    Aussi, il me semble qu’avoir des liens hypertextes identifiables rapidement (variations de couleur ou soulignage par exemple) est une bonne pratique. Je me suis rendu compte aujourd’hui qu’il y avait autant de liens dans tes articles.

  • zed

    @toto : Les liens sont identifiables par rapport au texte (certes un rouge assez sombre), et ils se soulignent au passage de la souris. L’article précise que 2 workers est le nombre par défaut et la doc que 10 est le nombre max de workers.

    Au passage quelle est la traduction “officielle” de worker en français ?

  • Sam Post author

    @zed : je dirais “processus dédié” mais à vrai dire je connais rien d’officiel.

  • kontre

    Pourquoi la réponse avec un score de -1 est en premier pour la question corespondante sur IndexError ? Y’a comme un souci.

  • Rififi

    Bonjour,

    est-ce que par hasard il y aurait un moyen de passer des arguments au callback ?

    Merci

  • Sam Post author

    La solution de cet article est plus pratique si on peut se permettre de rajouter une dépendance.

  • Rififi

    Ok, merci beaucoup, ça m’a aidé. Cependant je rencontre un problème plutôt inattendu. J’ai l’impression que les futures ne ferment pas les connections. Quand on lance beaucoup de futures, et qu’on fait un “lsof -i | grep “python” | wc -l”, on se rend compte que le nombre de fichiers ne fait qu’augmenter. Dans certains cas, ça dépasse ce que l’OS peut supporter, et le programme crashe. Est-ce que c’est normal ?

  • Sam Post author

    Je ne sais pas, faut regarder le code et checker si les futures ferment les connections ou si il faut le faire manuellement à l’obtention du résultat.

  • Rififi

    Ok, j’ai trouvé:

    “`

    headers = {‘User-agent’: ‘Mozilla/5.0’,

    ‘Connection’: ‘close’}

    future = self.session.get(url, headers=headers)

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.