
HTTPX: O Sucessor do Requests
6 min read
A biblioteca requests tem sido indiscutivelmente a ferramenta das requisições HTTP em Python. Sua API tornou a interação com APIs uma tarefa trivial, e ela se tornou a base de incontáveis scripts de automação de redes. No entanto, o mundo da programação e das redes evoluiu. Aplicações modernas exigem alta concorrência e eficiência, e é aqui que o requests, com sua natureza puramente síncrona, começa a mostrar sua idade.
É nesse cenário que surge o HTTPX, uma biblioteca de cliente HTTP de nova geração para Python. O HTTPX oferece uma API amplamente compatível, o que torna a migração quase indolor, mas adiciona superpoderes que são cruciais para a automação de redes moderna: suporte nativo a async/await e HTTP/2
Vamos focar nas vantagens do httpx e mostrar por que ele deve se tornar a sua nova ferramenta padrão para interagir com APIs de rede.
Requests vs. HTTPX: Uma Comparação Direta
| Característica | requests | httpx |
| API | Síncrona | Síncrona e Assíncrona |
| Protocolo HTTP | HTTP/1.1 | HTTP/1.1 e HTTP/2 |
| Performance | Excelente para tarefas sequenciais. | Superior para tarefas concorrentes (I/O-bound). |
| Dependências | Nenhuma para funcionalidades básicas. | httpcore, sniffio, anyio (para async). |
| Compatibilidade | Padrão de fato, enorme ecossistema. | API quase 100% compatível com requests. |
| Uso | Menos ideal para frameworks web assíncronos. | Ideal para FastAPI, Starlette… |
Vantagem 1: Programação Assíncrona (async/await)
A maior limitação do requests é que ele é síncrono. Quando você faz uma requisição, seu código para e espera a resposta do servidor antes de continuar. Em automação de redes, isso é um gargalo fatal. Imagine precisar consultar a API de 100 switches para obter o status de suas interfaces. Com requests, você faria isso em série:
sequenceDiagram
participant Script
participant Switch1
participant Switch2
participant Switch100
Script->>Switch1: GET /api/interfaces
Note right of Script: Espera...
Switch1-->>Script: Resposta
Script->>Switch2: GET /api/interfaces
Note right of Script: Espera...
Switch2-->>Script: Resposta
Note over Script: ...continua sequencialmente...
Script->>Switch100: GET /api/interfaces
Note right of Script: Espera...
Switch100-->>Script: Resposta
Se cada requisição levar 1 segundo, o processo todo levará 100 segundos. A maior parte desse tempo é gasta esperando a rede e o servidor (I/O-bound).
O HTTPX com async resolve isso. Ele permite que você dispare todas as 100 requisições de uma vez e espere pelas respostas concorrentemente. Enquanto uma requisição está esperando pela rede, o programa pode trabalhar em outra.
sequenceDiagram
participant Script
participant Switch1
participant Switch2
participant Switch100
Script->>Switch1: GET /api/interfaces
Script->>Switch2: GET /api/interfaces
Script->>Switch100: GET /api/interfaces
Note right of Script: Dispara todas as requisições
Switch1-->>Script: Resposta
Switch2-->>Script: Resposta
Switch100-->>Script: Resposta
Note right of Script: Processa as respostas à medida que chegam
Com async, o tempo total será próximo ao da requisição mais longa, talvez pouco mais de 1 segundo, em vez de 100 segundos. Uma melhoria de performance de quase 100x.
Caso Prático: Consultando Múltiplos Dispositivos no NetBox
Vamos buscar os detalhes de 3 dispositivos diferentes no NetBox, primeiro com requests e depois com httpx.
Com requests (Sequencial):
import requests
import time
NETBOX_URL = "https://demo.netbox.dev/api"
DEVICE_IDS = [1, 2, 3]
def fetch_devices_sync():
start_time = time.time()
for device_id in DEVICE_IDS:
response = requests.get(f"{NETBOX_URL}/dcim/devices/{device_id}/")
print(f"Dispositivo {device_id}: {response.json()["name"]}")
duration = time.time() - start_time
print(f"\nVersão síncrona levou: {duration:.2f} segundos")
fetch_devices_sync()
Com httpx (Concorrente):
import httpx
import asyncio
import time
NETBOX_URL = "https://demo.netbox.dev/api"
DEVICE_IDS = [1, 2, 3]
async def fetch_device(client, device_id):
response = await client.get(f"{NETBOX_URL}/dcim/devices/{device_id}/")
print(f"Dispositivo {device_id}: {response.json()["name"]}")
async def fetch_devices_async():
start_time = time.time()
async with httpx.AsyncClient() as client:
tasks = [fetch_device(client, device_id) for device_id in DEVICE_IDS]
await asyncio.gather(*tasks)
duration = time.time() - start_time
print(f"\nVersão assíncrona levou: {duration:.2f} segundos")
asyncio.run(fetch_devices_async())
Embora para 3 dispositivos a diferença possa ser pequena, para 100 ou 1000, a versão assíncrona será ordens de magnitude mais rápida.
Vantagem 2: Suporte a HTTP/2
O HTTP/2 é uma revisão majoritária do protocolo HTTP que traz melhorias significativas de performance, especialmente o multiplexing. Com HTTP/1.1, se você precisa de 5 recursos de um mesmo servidor, o navegador (ou seu cliente HTTP) abre múltiplas conexões TCP, o que é custoso. Com HTTP/2, todas essas requisições podem ser enviadas concorrentemente sobre uma única conexão TCP
Para APIs modernas que são servidas sobre HTTP/2 (como muitas APIs gRPC-web e de provedores de nuvem), usar um cliente que suporte o protocolo pode reduzir a latência e o consumo de recursos.
Com httpx, habilitar HTTP/2 é trivial:
import httpx
# O cliente tentará negociar HTTP/2 com o servidor
with httpx.Client(http2=True) as client:
response = client.get("https://http2.pro/api/v1")
print(response.http_version)
# Output: "HTTP/2"
A biblioteca requests não possui suporte nativo a HTTP/2.
Migrando de requests para httpx: Um Cheatsheet
A beleza do httpx é que sua API síncrona é um substituto quase direto para o requests. A migração é incrivelmente fácil.
requests | httpx (Síncrono) | httpx (Assíncrono) |
import requests | import httpx | import httpx & import asyncio |
requests.get(url) | httpx.get(url) | await client.get(url) |
requests.post(url, json=data) | httpx.post(url, json=data) | await client.post(url, json=data) |
response.json() | response.json() | response.json() |
response.status_code | response.status_code | response.status_code |
response.text | response.text | response.text |
requests.Session() | httpx.Client() | httpx.AsyncClient() |
with requests.Session() as s: | with httpx.Client() as c: | async with httpx.AsyncClient() as c: |
Passo 1: Migração Síncrona
Na maioria dos casos, você pode simplesmente substituir import requests por import httpx e seu código continuará funcionando.
Passo 2: Adotando o Async (Onde faz sentido)
Para as partes do seu código que fazem múltiplas chamadas de API, refatore-as para usar async def, httpx.AsyncClient, e asyncio.gather para obter os ganhos de performance.
Conclusão: Por que o HTTPX é o Futuro para NetDevOps
O requests é uma biblioteca fantástica e serviu à comunidade Python por muitos anos. Ela não está "morta" e ainda é perfeitamente adequada para scripts simples e sequenciais. No entanto, a automação de redes moderna é inerentemente concorrente. Precisamos interagir com dezenas, centenas ou milhares de dispositivos e APIs o mais rápido possível.
O HTTPX não é apenas uma alternativa; é a evolução natural. Ele nos dá:
Performance: A capacidade de executar operações de I/O em paralelo com
async.Eficiência: Suporte a HTTP/2 para interações mais rápidas com APIs modernas.
Flexibilidade: Uma única biblioteca que pode operar tanto de forma síncrona quanto assíncrona.
Familiaridade: Uma API que se sente em casa para qualquer um que já usou
requests.
Para qualquer novo projeto de automação de redes que envolva interação com APIs, o httpx deveria ser sua escolha padrão. Comece usando sua API síncrona para uma transição fácil e, em seguida, adote o async nas partes do seu código que mais se beneficiarão da concorrência. Sua automação agradecerá pela velocidade.
Referências:
