Menu

Richieste HTTP in Python: la libreria requests (GET, POST, JSON)

Come fare richieste HTTP in Python con la libreria requests: GET, POST, parametri di query, header, corpi JSON e gestione degli errori.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Prendere dati da internet, in poche righe

Prima o poi la maggior parte dei programmi Python reali deve comunicare con qualcosa in rete: una REST API, un servizio meteo, un endpoint di GitHub, un download. La libreria standard può farlo (tramite urllib), ma lo strumento di fatto nel mondo Python è una libreria di terze parti chiamata requests. La sua API è così tanto più amichevole che vale il singolo pip install.

pip install requests

Se non stai già lavorando dentro un ambiente virtuale, creane uno prima: mantiene questa installazione limitata al tuo progetto.

Una prima richiesta GET

Tre righe: chiama get, controlla il codice di stato, guarda il corpo. response.text è il corpo della risposta come stringa. Qui è troncato perché il JSON completo è lungo.

I codici di stato da riconoscere, a livello di promemoria:

  • 200: OK, tutto ha funzionato.
  • 201: Created (risposta comune a una POST).
  • 301 / 302: reindirizzamenti; per impostazione predefinita requests li segue in automatico.
  • 400: richiesta non valida; qualcosa in quello che hai inviato era sbagliato.
  • 401 / 403: non autenticato / non autorizzato.
  • 404: la risorsa non esiste.
  • 429: limite di richieste superato; rallenta.
  • 500: errore del server.

Interpretare una risposta JSON

Quando l'endpoint restituisce JSON, chiama .json() sulla risposta: interpreta il corpo e ti restituisce un dict (o una lista):

Dietro le quinte, .json() equivale a json.loads(response.text): solo una scorciatoia per il caso più comune.

Inviare parametri di query

Non attaccare a mano ?key=value&... all'URL. Passa un dict a params=:

requests si occupa della codifica dell'URL al posto tuo: spazi, caratteri speciali e Unicode funzionano tutti in sicurezza.

L'URL effettivamente inviato è disponibile in response.url, comodo per il debug.

Richieste POST con corpo JSON

Per inviare dati (creare risorse, inviare moduli, chiamare endpoint che modificano qualcosa) usa requests.post:

import requests

payload = {
    "title": "Docs update",
    "body": "Added HTTP requests page.",
    "labels": ["docs"],
}

response = requests.post(
    "https://api.example.com/issues",
    json=payload,
    headers={"Authorization": "Bearer YOUR_TOKEN"},
)

print(response.status_code)
print(response.json())

L'argomento json= fa due cose: serializza payload in JSON e imposta Content-Type: application/json. Potresti fare entrambe le cose a mano con data=json.dumps(payload) e un header esplicito, ma json= è la scorciatoia idiomatica.

Per i dati codificati come modulo (quelli che invia un classico form HTML), usa invece data=:

requests.post("https://example.com/login", data={"user": "rosa", "password": "..."})

Passa gli header personalizzati come dict:

import requests

response = requests.get(
    "https://api.example.com/profile",
    headers={
        "Authorization": "Bearer abc123",
        "User-Agent": "my-tool/1.0",
    },
)

La maggior parte delle API vuole un header Authorization per l'autenticazione. Lo schema esatto (Bearer, Basic, Token) è indicato nella loro documentazione.

I timeout non sono facoltativi

Per impostazione predefinita, requests aspetta una risposta all'infinito. In un programma reale, questo trasforma un server instabile in uno script bloccato. Passa sempre un timeout:

import requests

try:
    response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
    print("Server took too long.")

Il numero è in secondi. timeout=5 significa "rinuncia se non abbiamo una risposta entro 5 secondi". Per un controllo più fine puoi passare una tupla (connect_timeout, read_timeout).

Gestione degli errori

Possono capitare due tipi di problemi:

  1. Errori a livello HTTP (4xx, 5xx): il server ha risposto, ma la risposta è un errore. Te lo dice response.status_code.
  2. Problemi a livello di rete: timeout, errori DNS, host irraggiungibili. Questi sollevano eccezioni.

Lo schema idiomatico li combina entrambi:

raise_for_status() non fa niente con le risposte 2xx e altrimenti solleva un'eccezione. RequestException è la classe base di ogni errore sollevato da requests: un'unica cattura per "qualsiasi cosa sia andata storta parlando con questo endpoint".

Scaricare un file

Per download binari grandi, leggi la risposta in streaming così non resta tutta in memoria:

import requests

url = "https://example.com/large.zip"

with requests.get(url, stream=True, timeout=30) as r:
    r.raise_for_status()
    with open("large.zip", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

stream=True dice a requests di non precaricare il corpo. iter_content(chunk_size=...) restituisce il corpo un blocco alla volta, che scrivi direttamente su disco.

Sessioni: riusare connessioni e impostazioni

Se devi fare diverse richieste allo stesso servizio, usa una Session. Riusa la connessione TCP sottostante (più veloce) e ti permette di impostare i valori predefiniti una volta sola:

import requests

session = requests.Session()
session.headers.update({"Authorization": "Bearer abc123"})

# Ogni richiesta fatta con questa sessione porta con sé l'header.
a = session.get("https://api.example.com/users/1")
b = session.get("https://api.example.com/users/2")
c = session.post("https://api.example.com/users", json={"name": "Rosa"})

Per gli script che chiamano la stessa API decine di volte, una sessione dà un'accelerazione notevole.

Un esempio realistico: un piccolo client GitHub

Recuperare l'ultima release di un repository:

import requests

def latest_release(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    data = response.json()
    return {
        "tag": data["tag_name"],
        "name": data["name"],
        "published": data["published_at"],
        "url": data["html_url"],
    }

release = latest_release("python", "cpython")
print(release)

Meno di quindici righe: costruisci l'URL, fai la richiesta, controlla gli errori, estrai i campi che ti interessano. È la forma della maggior parte dei client API che scriverai.

E urllib?

Il modulo urllib.request della libreria standard può fare tutto quello che fa requests, con più righe e meno comodità. Se proprio non puoi aggiungere una dipendenza, è lì:

import json
import urllib.request

with urllib.request.urlopen("https://api.github.com/repos/python/cpython") as r:
    data = json.loads(r.read().decode("utf-8"))

print(data["name"])

Per qualsiasi cosa vada oltre uno script veloce, requests (o httpx se ti serve async) vale l'installazione.

Qualche buona abitudine

  • Imposta sempre un timeout. Nessuna eccezione.
  • Usa raise_for_status() negli script che si aspettano un successo: trasforma una risposta sbagliata in un'eccezione ben visibile.
  • Passa dict a params= e json=, non stringhe costruite a mano.
  • Raggruppa le chiamate collegate in una Session quando chiami molte volte la stessa API.
  • Registra response.status_code e response.text durante il debug: di solito il corpo ti dice esattamente cosa non è piaciuto al server.

Prossimo argomento: date e orari

Con requests nella tua cassetta degli attrezzi puoi comunicare con qualsiasi API web moderna, scaricare file e costruire piccole integrazioni tra servizi. Insieme alle pagine precedenti su JSON e CSV, ora hai il ciclo completo "prendi i dati, leggili, fanne qualcosa, riscrivili": la forma di un numero enorme di script Python reali. Però la maggior parte di questi dati ha un timestamp, e la prossima pagina spiega come Python rappresenta date, orari e le trappole dei fusi orari da evitare.

Domande frequenti

Come faccio una richiesta HTTP in Python?

Installa la libreria requests con pip install requests, poi chiama requests.get(url) per una GET o requests.post(url, json=...) per una POST. L'oggetto risposta ha .status_code, .text, .json() e .headers. Esempio: r = requests.get('https://api.example.com/users/1').

Meglio requests o urllib?

requests per tutto quello che scriveresti a mano: la sua API è molto più amichevole. urllib è integrato e va bene quando aggiungere una dipendenza è impossibile, ma richiede più codice per cose come corpi JSON e sessioni. In produzione molti team usano anche httpx (una libreria compatibile con requests che supporta async).

Come invio JSON in una richiesta POST in Python?

Passa json={'key': 'value'} a requests.post(...). requests serializza il dict in JSON e imposta Content-Type: application/json per te. Non passare sia data= sia json=: scegline uno.

Come gestisco gli errori con la libreria requests?

Controlla response.status_code (200 significa successo), oppure chiama response.raise_for_status() per sollevare un'eccezione con i codici 4xx/5xx. I problemi a livello di rete (timeout, errori DNS) sollevano sottoclassi di requests.RequestException: cattura quella per coprire entrambi i casi.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA