Introduzione
Quando si lavora con le API in Python, non sempre la risposta è un semplice JSON da leggere con response.json(). In molti casi, l’endpoint restituisce un file: un PDF, un’immagine, un archivio ZIP, un CSV o un report generato al volo. In questi scenari, usare requests in modo corretto è fondamentale per evitare problemi di memoria, download incompleti o file corrotti.
Questo tutorial si concentra su un sotto-argomento molto pratico e spesso trascurato: il download e la gestione dei file da API con requests. Vedremo come scaricare contenuti binari, gestire file di grandi dimensioni con streaming, leggere correttamente gli header HTTP e salvare i file in modo robusto.
L’obiettivo è darti un approccio concreto e riutilizzabile per integrare API che espongono documenti o media, senza affidarti a soluzioni improvvisate.
Codice completo
Nel codice seguente simuliamo il download di un file da un’API pubblica. Il pattern, però, è valido per qualunque endpoint che restituisca contenuti binari.
import os
from pathlib import Path
import requests
def scarica_file_da_api(url: str, cartella_destinazione: str = "download") - Spiegazione
Analizziamo il codice passo per passo, perché il valore di questo approccio non sta solo nel “far funzionare il download”, ma nel farlo in modo scalabile e sicuro.
1. Uso di stream=True
Il parametro stream=True dice a requests di non scaricare tutto il contenuto subito. Questo è molto importante quando il file è grande, perché evita di occupare memoria inutilmente.
Invece di ricevere il file completo in un’unica volta, puoi leggerlo a blocchi con iter_content().
2. Controllo degli errori con raise_for_status()
Se l’API risponde con un codice HTTP di errore, come 404 o 500, raise_for_status() solleva un’eccezione. Questo ti permette di intercettare subito problemi lato server o URL errati, senza salvare file sbagliati sul disco.
3. Lettura dell’header Content-Disposition
Molte API che restituiscono file indicano il nome corretto nel header Content-Disposition. È una buona pratica leggerlo, perché l’ultima parte dell’URL non sempre rappresenta il nome reale del file.
Se l’header non è disponibile, il codice usa un fallback basato sull’URL. Questo rende la funzione più robusta.
4. Scrittura a blocchi con iter_content()
Il metodo iter_content(chunk_size=8192) legge il contenuto in blocchi da 8 KB. Questo è uno standard molto usato perché bilancia bene velocità e uso della memoria.
Il controllo if chunk evita di scrivere blocchi vuoti, che possono comparire in alcune risposte streaming.
5. Uso di pathlib
Per gestire i percorsi ho usato pathlib.Path invece di concatenare stringhe manualmente. È una scelta moderna e più leggibile, soprattutto quando vuoi creare cartelle o costruire percorsi in modo portabile tra Windows, macOS e Linux.
6. Gestione delle eccezioni
Nel blocco finale, requests.exceptions.RequestException intercetta gli errori più comuni della libreria: problemi di rete, timeout, DNS, connessione rifiutata e risposte HTTP gestite tramite raise_for_status().
Best practice
Quando scarichi file da API, ci sono alcune regole pratiche che conviene seguire sempre.
- Usa sempre timeout: senza timeout, una richiesta può rimanere bloccata a lungo in caso di problemi di rete.
- Preferisci lo streaming per file grandi: evita di usare response.content per file pesanti, perché carica tutto in memoria.
- Valida il nome del file: se il nome arriva dall’API, controlla che non contenga caratteri pericolosi o percorsi non desiderati.
- Controlla il tipo di contenuto: prima di salvare, verifica Content-Type se vuoi essere sicuro che la risposta sia davvero un file atteso.
- Usa cartelle dedicate: separare i download dal resto del progetto aiuta a mantenere ordine e facilita la pulizia.
- Gestisci correttamente le eccezioni: non limitarti a stampare l’errore; in un progetto reale potresti fare retry, logging o notifiche.
- Chiudi sempre le risposte: il blocco with requests.get(...) garantisce la chiusura automatica della connessione.
Un’altra pratica importante è evitare di fidarti ciecamente del nome file fornito dall’API. In ambienti reali, specialmente quando l’API è esterna o poco controllata, è bene normalizzare il nome e rimuovere eventuali caratteri non validi.
Se devi gestire download frequenti, puoi anche integrare un sistema di retry con backoff progressivo. Requests da solo non lo fa automaticamente, ma puoi combinarlo con strumenti come urllib3 Retry o librerie esterne.
Riepilogo
Scaricare file da API con requests è un’operazione comune, ma va affrontata con attenzione. I punti chiave da ricordare sono:
- stream=True è la scelta giusta per file grandi o sconosciuti.
- iter_content() permette di scrivere il file a blocchi.
- raise_for_status() evita di salvare risposte di errore come se fossero file validi.
- Content-Disposition può contenere il nome corretto del file.
- timeout, eccezioni e pathlib migliorano robustezza e leggibilità.
Questo approccio è utile in molti contesti: download di report, esportazioni CSV, allegati, immagini generate dal server, documenti firmati o archivi compressi. Una volta compreso il pattern, potrai riutilizzarlo facilmente in script di automazione, backend e tool interni.
Approfondisci con risorse ufficiali
- Documentazione ufficiale di Requests: guida completa a richieste, streaming, sessioni ed eccezioni.
- HTTP headers su MDN: per capire meglio header come Content-Type e Content-Disposition.
- Python pathlib: documentazione ufficiale per la gestione moderna dei percorsi.
- RFC HTTP: utile per approfondire il significato dei codici di stato e degli header.
Se vuoi, nel prossimo passo posso scrivere un tutorial complementare su come caricare file verso un’API con requests oppure su come implementare retry automatici e backoff esponenziale.
