Requests / HTTPX / AIOHTTP : Comparaison détaillée

Découvrez Requests, HTTPX et AIOHTTP, les clients HTTP les plus populaires de Python, et trouvez celui qui convient le mieux à vos besoins en matière de collecte de données.
14 min de lecture
Requests vs. HTTPX vs. AIOHTTP blog image

Python est l’hôte d’une grande variété de clients HTTP. Pour ceux d’entre vous qui ne connaissent pas le protocole HTTP (Hypertext Transfer Protocol), il s’agit du cadre sous-jacent de l’ensemble du web.

Aujourd’hui, nous allons comparer trois des clients HTTP les plus populaires de Python : Requests, HTTPX et AIOHTTP. Si vous souhaitez en savoir plus sur les autres solutions disponibles, cliquez ici.

Bref aperçu

Requests est le client HTTP standard pour Python. Il utilise des opérations bloquantes et synchrones qui facilitent son utilisation. HTTPX est un client asynchrone plus récent, conçu à la fois pour la rapidité et la facilité d’utilisation. AIOHTTP existe depuis plus de dix ans. C’est l’un des premiers clients HTTP asynchrones les mieux pris en charge par Python.

Caractéristiques Requests HTTPS AIOHTTP
Structure Synchronisation/blocage Asynchrone/non bloquant Asynchrone/non bloquant
Sessions Oui Oui Oui
Simultanéité Non Oui Oui
Prise en charge de HTTP/2 Non Oui Oui
Performances Faibles Élevées Élevées
Tentatives Automatique Automatique Manuel
Prise en charge du délai d’attente Par demande Prise en charge complète Prise en charge complète
Prise en charge de proxies Oui Oui Oui
Facilité d’utilisation Facile Difficile Difficile
Cas d’utilisation Projets simples/prototypage Performances élevées Performances élevées

Requêtes Python

Python Requests est très intuitif et facile à utiliser. Si des performances de pointe ne sont pas nécessaires, c’est la solution pour effectuer des requêtes HTTP en Python. Il est largement utilisé, facile à comprendre et c’est le client HTTP Python le mieux documenté au monde.

Vous pouvez l’installer avec la commande ci-dessous.

Installation

pip install requests

Les demandes prennent en charge le protocole HTTP standard et même une partie de la gestion des sessions. Les sessions vous permettent de créer une connexion persistante avec un serveur. Cela vous permet de scraper vos données beaucoup plus rapidement et plus efficacement qu’en effectuant des requêtes individuelles. Si vous souhaitez simplement effectuer des requêtes de base (GET, POST, PUT et DELETE) et que vous n’avez pas besoin de performances élevées, la bibliothèque Requests peut répondre à tous vos besoins en matière de HTTP.

Partout sur le web, vous trouverez des guides sur l’intégration de proxies, les agents utilisateurs, etc.

Vous trouverez ci-dessous quelques exemples d’utilisation de base.

import requests

# make a simple request
response = requests.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code)

# use a session for multiple requests to the same server
with requests.Session() as client:
    for get_request in range(1000):
        response = client.get("https://jsonplaceholder.typicode.com/posts")
        print(response.status_code)

Requests n’est pas à la hauteur lorsqu’il s’agit d’opérations asynchrones. En asynchrone, vous pouvez envoyer un lot de requêtes en une seule fois. Vous n’avez plus qu’à attendre de toutes les recevoir. Avec les requêtes synchrones, vous ne pouvez effectuer qu’une seule requête à la fois et vous devez attendre la réponse du serveur avant d’effectuer une autre requête. Si votre programme doit effectuer un grand nombre de requêtes HTTP, l’utilisation de Requests entraînera certaines limitations inhérentes à votre code.

HTTPX

HTTPX est la plus récente et la plus moderne de ces trois bibliothèques. Cela nous donne une prise en charge complète pour les opérations asynchrones dès le départ. Cela dit, la syntaxe reste conviviale et intuitive. Utilisez HTTPX si Requests n’est pas suffisant et que vous souhaitez améliorer les performances sans trop de difficultés d’apprentissage.

Avec asyncio (entrée et sortie asynchrones), vous pouvez écrire du code qui tire pleinement parti des réponses asynchrones avec le mot-clé await . Cela nous permet de poursuivre nos opérations sans bloquer tout le reste du code pendant que nous attendons que les choses se passent. Avec ces opérations asynchrones, vous pouvez envoyer de grands lots de requêtes à la fois. Au lieu de faire une demande à la fois, vous pouvez en faire 5, 50 ou même 100 !

Installation

pip install httpx

Voici quelques exemples pour commencer à utiliser HTTPX.

import httpx
import asyncio

# synchronous response
response = httpx.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code)

# basic async session usage
async def main():
    async with httpx.AsyncClient() as client:
        for get_request in range(1000):
            response = await client.get("https://jsonplaceholder.typicode.com/posts")
            print(response.status_code)
asyncio.run(main())

HTTPX est un excellent choix pour l’écriture de nouveaux codes, mais il comporte ses propres limites. En raison de sa syntaxe, il peut être difficile de remplacer votre base de code Requests existante par HTTPX. L’écriture d’un code asynchrone nécessite un certain nombre de procédures et, à moins que vous ne fassiez des milliers de requêtes, cela ne vaut souvent pas la peine de consacrer plus de temps au développement.

Par rapport à AIOHTTP (comme vous le verrez bientôt), HTTPX n’est pas aussi rapide. Si vous souhaitez construire un serveur web ou un réseau complexe, HTTPX n’est pas un bon choix en raison de son écosystème immature. HTTPX convient mieux aux nouvelles applications côté client dotées de fonctionnalités modernes telles que HTTP/2.

AIOHTTP

En matière de programmation asynchrone, AIOHTTP est largement utilisé en Python depuis longtemps. Que vous utilisiez un serveur, une application côté client ou un réseau distribué, AIOHTTP peut répondre à tous ces besoins. Cependant, parmi les trois (Requests, HTTPX et AIOHTTP), c’est AIOHTTP qui présente la courbe d’apprentissage la plus raide.

Nous nous concentrons sur une utilisation simple côté client, nous ne plongerons donc pas trop loin dans les méandres d’AIOHTTP. Comme HTTPX, AIOHTTP permet d’envoyer des lots de requêtes. Toutefois, contrairement à HTTPX, AIOHTTP ne prend pas en charge les requêtes synchrones. Mais n’en faites pas trop à la fois… vous ne voudriez pas vous faire bannir par le serveur.

Installation

pip install aiohttp

Regardez l’utilisation de base ci-dessous.

import aiohttp
import asyncio

# make a single request
async def main():
    async with aiohttp.ClientSession() as client:
        response = await client.get("https://jsonplaceholder.typicode.com/posts")
        print(response)
        
asyncio.run(main())


# basic async session usage
async def main():
    with aiohttp.ClientSession() as client:
        for response in range(1000):
            response = await client.get("https://jsonplaceholder.typicode.com/posts")
            print(response)
            
asyncio.run(main())

AIOHTTP est strictement asynchrone. Comme vous pouvez le voir dans le code ci-dessus, notre exemple de demande unique nécessite beaucoup plus de code que nos deux premiers exemples. Quel que soit le nombre de requêtes à effectuer, nous devons mettre en place une session asynchrone, c’est pourquoi il est recommandé de combiner AIOHTTP avec des proxies. Pour une demande unique, c’est tout à fait exagéré.

Malgré ses nombreux atouts, AIOHTTP ne remplacera pas complètement Requests de Python, à moins que vous ne deviez traiter simultanément de très nombreuses requêtes entrantes et sortantes. Dans ce cas, AIOHTTP augmentera considérablement vos performances. Utilisez AIOHTTP lorsque vous construisez des applications complexes et que vous avez besoin d’une communication rapide comme l’éclair. Cette bibliothèque est idéale pour les serveurs, les réseaux distribués et les applications de web scraping très complexes.

Comparaison des performances

Maintenant, nous allons construire un petit programme en utilisant chaque bibliothèque. Les exigences sont simples : ouvrir une session client et effectuer 1000 requêtes API.

Tout d’abord, nous devons créer notre session avec le serveur. Ensuite, nous effectuons 1000 requêtes. Nous aurons deux tableaux : un pour les bonnes réponses et un pour les mauvaises réponses. Une fois l’exécution terminée, nous afficherons le décompte complet des bonnes et des mauvaises demandes. Si nous recevons de mauvaises requêtes, nous affichons le code d’état correspondant sur la console.

Avec nos exemples asynchrones (HTTPX et AIOHTTP), nous utiliserons une fonction chunkify() . Ceci est utilisé pour diviser un tableau en morceaux. Nous exécutons ensuite les requêtes par lots. Par exemple, si nous voulons exécuter nos requêtes par lots de 50, nous utiliserons chunkify() pour créer le lot et nous utiliserons process_chunk() pour exécuter les 50 requêtes en une seule fois.

Jetez un coup d’œil à ces fonctions ci-dessous.

def chunkify(iterable, size):
    iterator = iter(iterable)
    while chunk := list(islice(iterator, size)):
        yield chunk
        
        
async def process_chunk(client, urls, retries=3):
    tasks = [fetch(client, url, retries) for url in urls]
    return await asyncio.gather(*tasks)

Requests

Voici notre code utilisant Requests. C’est très simple, comparé aux deux exemples asynchrones que nous utiliserons plus tard. Nous ouvrons une session et utilisons une boucle « for » pour itérer à travers les requêtes.

import requests
import json
from datetime import datetime

start_time = datetime.now()
good_responses = []
bad_responses = []

with requests.Session() as client:

    for get_request in range(1000):
        response = client.get("https://jsonplaceholder.typicode.com/posts")
        status_code = response.status_code
        if status_code  == 200:
            good_responses.append(status_code)
        else:
            bad_responses.append(status_code)

end_time = datetime.now()

print("----------------Requests------------------")
print(f"Time elapsed: {end_time - start_time}")
print(f"Good Responses: {len(good_responses)}")
print(f"Bad Responses: {len(bad_responses)}")

for status_code in set(bad_responses):
    print(status_code)
Résultats des requêtes Python

Il a fallu un peu plus de 51 secondes pour finaliser toutes les requêtes. Cela représente environ 20 demandes par seconde. Si vous n’utilisez pas de session, vous pouvez compter sur 2 secondes par demande. C’est assez performant.

HTTPX

Voici le code pour HTTPX. Nous utilisons chunkify() et process_chunk() comme nous l’avons mentionné précédemment.

import httpx
import asyncio
from datetime import datetime
from itertools import islice

def chunkify(iterable, size):
    iterator = iter(iterable)
    while chunk := list(islice(iterator, size)):
        yield chunk

async def fetch(client, url, retries=3):
    """Fetch a URL with retries."""
    for attempt in range(retries):
        try:
            response = await client.get(url)
            return response.status_code
        except httpx.RequestError as e:
            if attempt < retries - 1:
                await asyncio.sleep(1)
            else:
                return f"Error: {e}"

async def process_chunk(client, urls, retries=3):
    tasks = [fetch(client, url, retries) for url in urls]
    return await asyncio.gather(*tasks)

async def main():
    url = "https://jsonplaceholder.typicode.com/posts"
    total_requests = 1000
    chunk_size = 50

    good_responses = []
    bad_responses = []

    async with httpx.AsyncClient(timeout=10) as client:
        start_time = datetime.now()

        urls = [url] * total_requests
        for chunk in chunkify(urls, chunk_size):
            results = await process_chunk(client, chunk)
            for status in results:
                if isinstance(status, int) and status == 200:
                    good_responses.append(status)
                else:
                    bad_responses.append(status)

        end_time = datetime.now()

        print("----------------HTTPX------------------")
        print(f"Time elapsed: {end_time - start_time}")
        print(f"Good Responses: {len(good_responses)}")
        print(f"Bad Responses: {len(bad_responses)}")

        if bad_responses:
            print("Bad Status Codes or Errors:")
            for error in set(bad_responses):
                print(error)

asyncio.run(main())

Voici le résultat obtenu lorsque nous utilisons HTTPX. Comparé à Requests, c’est époustouflant. Le temps total est d’un peu plus de 7 secondes. Cela représente 139,47 demandes par seconde. HTTPX nous a permis d’obtenir des performances environ 7 fois supérieures à celles de Requests.

Résultats HTTPX

AIOHTTP

Nous allons maintenant faire le même exercice en utilisant AIOHTTP. Nous suivons la même structure de base que celle utilisée dans l’exemple HTTPX. La seule différence majeure réside dans le fait que le client AIOHTTP remplace le client HTTPX.

import aiohttp
import asyncio
from datetime import datetime
from itertools import islice

def chunkify(iterable, size):
    iterator = iter(iterable)
    while chunk := list(islice(iterator, size)):
        yield chunk

async def fetch(session, url, retries=3):
    for attempt in range(retries):
        try:
            async with session.get(url) as response:
                return response.status
        except aiohttp.ClientError as e:
            if attempt < retries - 1:
                await asyncio.sleep(1)
            else:
                return f"Error: {e}"

async def process_chunk(session, urls):
    tasks = [fetch(session, url) for url in urls]
    return await asyncio.gather(*tasks)

async def main():
    url = "https://jsonplaceholder.typicode.com/posts"
    total_requests = 1000
    chunk_size = 50

    good_responses = []
    bad_responses = []

    async with aiohttp.ClientSession() as session:
        start_time = datetime.now()

        urls = [url] * total_requests
        for chunk in chunkify(urls, chunk_size):
            results = await process_chunk(session, chunk)
            for status in results:
                if isinstance(status, int) and status == 200:
                    good_responses.append(status)
                else:
                    bad_responses.append(status)

        end_time = datetime.now()

        print("----------------AIOHTTP------------------")
        print(f"Time elapsed: {end_time - start_time}")
        print(f"Good Responses: {len(good_responses)}")
        print(f"Bad Responses: {len(bad_responses)}")

        if bad_responses:
            print("Bad Status Codes or Errors:")
            for error in set(bad_responses):
                print(error)

asyncio.run(main())

AIOHTTP s’en est très bien sorti avec un peu plus de 4 secondes. Ce client HTTP a généré plus de 241 requêtes par seconde ! AIOHTTP est environ 10 fois plus rapide que Requests et près de 50 % plus rapide que HTTPX. En Python, AIOHTTP est dans une ligue à part en ce qui concerne les performances.

Résultats AIOHTTP

Comment les produits de Bright Data peuvent aider

Bright Data offre une gamme de solutions qui peuvent améliorer vos flux de travail basés sur le client HTTP, en particulier pour les opérations à forte intensité de données telles que l’exploration du Web, les demandes d’API et les intégrations à haute performance. Voici comment chaque produit s’intègre :

  • Proxies résidentiels – Les proxies résidentiels de Bright Data permettent d’éviter les blocages et les interdictions lors de la récupération de sites Web à l’aide de clients HTTP Python tels que AIOHTTP ou HTTPX. Ces proxys imitent le comportement de l’utilisateur réel et permettent d’accéder facilement à des contenus dynamiques ou soumis à des restrictions géographiques.
  • Web Scraper APIs – Au lieu de construire et de maintenir votre propre infrastructure de scraping, les Web Scraper APIs de Bright Data offrent un accès préconfiguré à des centaines de sites Web populaires. Cela vous permet de vous concentrer sur l’analyse des données plutôt que sur la gestion des requêtes, des tentatives et des interdictions. Il suffit d’utiliser un appel API pour obtenir directement des données structurées.
  • Ensembles de données prêts à l’emploi – Pour ceux qui ont besoin de points de données spécifiques mais qui veulent éviter le scraping, Bright Data propose des ensembles de données prêts à l’emploi adaptés à vos besoins. Ces ensembles de données comprennent des détails sur les produits, des prix et des avis, et sont immédiatement utilisables pour l’analyse du commerce électronique ou l’étude de marché.
  • Web Unlocker – Le Web Unlocker gère automatiquement les défis tels que les CAPTCHA, les mécanismes anti-bots et les modèles de demande complexes. Associez-le à des bibliothèques comme HTTPX ou AIOHTTP pour rationaliser le processus de scraping des sites web difficiles d’accès.
  • SERP API – Si vous extrayez des données des moteurs de recherche, l’API SERP de Bright Data simplifie le processus en offrant un accès fiable et en temps réel aux résultats de recherche, aux publicités et aux classements sans se soucier de l’infrastructure ou du blocage.

En intégrant les outils de Bright Data aux clients HTTP Python, vous pouvez construire des systèmes robustes et performants qui simplifient la collecte de données tout en surmontant les défis typiques du web scraping et de l’acquisition de données.

Conclusion

Dans le monde des clients HTTP, Requests est la norme, simplement en raison de sa facilité d’utilisation. Comparé à Requests, HTTPX est plus proche d’une voiture moderne que d’une calèche. Il nous offre un équilibre entre haute performance et facilité d’utilisation. AIOHTTP ressemblerait alors à une fusée. Vous ne devriez pas l’utiliser à moins que ce ne soit absolument nécessaire, mais c’est toujours, et de loin, le client HTTP le plus rapide.

Inscrivez-vous à Bright Data dès aujourd’hui pour débloquer des solutions de données puissantes et obtenir l’avantage concurrentiel dont votre entreprise a besoin. Tous les produits sont livrés avec un essai gratuit !

Aucune carte de crédit requise