Requests: HTTP Per Gli Esseri Umani

Pubblicato da Michele Saba

Oggi, come potete intuire dal titolo del post parliamo di Requests, un modulo per Python pensato e realizzato per effettuare richieste HTTP in maniera semplice e pratica.

Si tratta senza dubbio del modulo Numero Uno per effettuare questo genere di operazioni di rete, con oltre 38000 stelle su GitHub, 40 milioni di downloads e Testimonial che non hanno certo bisogno di introduzioni tra cui ad esempio, Microsoft e Google.

Abbiamo già avuto modo di utilizzare questo modulo qui nel blog di programmareinpython.it e sul Canale YouTube come parte di altri programmi che abbiamo scritto e analizzato, ma in questo video faremo svariati esempi specifici che vi saranno utili nella gran parte dei casi d'uso più comuni, permettendovi di scrivere programmi in grado di connettersi ad internet per inviare e ricevere dati ad esempio verso siti web e REST API.

Il codice utilizzato nel tutorial può essere trovato nel solito repository GitHub: https://github.com/pymike00/youtube-scripts--italian

Happy Coding! :D


Come installare il modulo requests

Creiamo un nuovo ambiente virtuale e installiamo requests tramite pip:

pip install requests

Utilizziamo la shell interattiva di Python per importare il modulo:

>>> import requests


Come effettuare richieste di tipo GET con requests

Effettuiamo una richiesta di tipo GET per la homepage di Google:

>>> response = requests.get("https://www.google.it")

Tramite l’oggetto response possiamo ora ottenere tutto ciò di cui possiamo avere bisogno riguardo a questa specifica richiesta che abbiamo effettuato: da dettagli tecnici, come ad esempio il codice di stato, all'eventuale contenuto della risposta. Accediamo allo status code in questo modo:

>>> print("Status Code: ", response.status_code)
Status code: 200

Vediamo che la richiesta è andata a buon fine perché otteniamo come status code 200, ma dobbiamo gestire anche i casi in cui si verificano degli errori. Requests ci mette a disposizione una scorciatoia per verificare se il codice di stato che otteniamo è minore di 400 (oltre il 400 infatti i codici di stato indicano che si è verificato un qualche tipo di errore):

>>> if response.ok:
...     print("Tutto ok!")
... else:
...     print("Qualcosa è andato storto...")
...

Qualora venga ricevuto del contenuto questo può essere visionato in maniera diversa a seconda del tipo: ad esempio per visionare il contenuto della risposta in byte possiamo scrivere:

>>> print(response.content)

b'<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="it"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title> ...

Oppure possiamo convertire in stringa il contenuto in questo modo per ottenere il codice HTML della Homepage di Google:

>>> print(response.text)

<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="it"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title><script nonce="-w6DiK7IerNjtXO0j8jKhQ">(function() ...


Ricevere dati in formato JSON tramite requests

Invece per ricevere i dati in formato JSON scriviamo:

>>> response.json()

Possiamo visionare gli elementi degli Header nel messaggio di risposta per ottenere informazioni utili come ad esempio il tipo di contenuto ricevuto quindi:

>>> print("Content-Type: ", response.headers["Content-Type"])
Content-Type:  text/html; charset=ISO-8859-1

Oppure visionare tutto il contenuto degli Header:

>>> print("Headers: ", response.headers)
Headers:  {'Date': 'Wed, 29 Mar 2023 07:26:12 GMT', 'Expires': '-1', 'Cache-Control': 'private, max-age=0', 'Content-Type': 'text/html; charset=ISO-8859-1', 'Content-Security-Policy-Report-Only': "object-src 'none';base-uri 'self';script-src 'nonce-c6E5xBv0LzVFlrap4U1DNg' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp", 'P3P': 'CP="This is not a P3P policy! See g.co/p3phelp for more info."', 'Content-Encoding': 'gzip', 'Server': 'gws', 'X-XSS-Protection': '0', 'X-Frame-Options': 'SAMEORIGIN', 'Set-Cookie': 'SOCS=CAAaBgiA_42hBg; expires=Sat, 27-Apr-2024 07:26:12 GMT; path=/; domain=.google.it; Secure; SameSite=lax, AEC=AUEFqZcWrISSKrtHca10MbVVtTdfZDPFYKxgvGh3rglNUs-lS5fSRkIE2yE; expires=Mon, 25-Sep-2023 07:26:12 GMT; path=/; domain=.google.it; Secure; HttpOnly; SameSite=lax, __Secure-ENID=11.SE=T2lXBDkBgH6U6XHUC8RYLbTW7Du5Gu_JZqtqKPDp5MfrTxy7A4bh6f07xEWQUNtsfHTnCM5UMwu4UNzylE91MYtj64r9L6C1cjizy30Mzb74ULKqznLZiSC-u0UktxcRUSb098_XLlMzkQnglExsVS37p4NiK0WBQmlWrDL9ZAM; expires=Sat, 27-Apr-2024 23:44:30 GMT; path=/; domain=.google.it; Secure; HttpOnly; SameSite=lax, CONSENT=PENDING+205; expires=Fri, 28-Mar-2025 07:26:12 GMT; path=/; domain=.google.it; Secure', 'Alt-Svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000', 'Transfer-Encoding': 'chunked'}


Creiamo un programma che scarichi i tassi di conversione tra valute tramite un’API

Creiamo un nuovo script di Python e comunichiamo con un’API per ottenere gli ultimi tassi di conversione tra valute.


ATTENZIONE: l’API utilizzata nel video adesso richiede un token di autenticazione per essere utilizzata, e si può ottenere effettuando l’accesso al loro sito. Se volete comunque provare a scaricare i tassi di cambio senza iscrivervi da nessuna parte, in alternativa potete utilizzare l'API open-source di Frankfurter, ma tenete a mente che con questa potreste dover adattare il codice mostrato per ottenere gli stessi risultati. API Endpoint:

https://api.frankfurter.app/latest


Definiamo una funzione get_json() che accetta come parametro un endpoint ed effettua una richiesta di tipo GET verso quest’ultimo per ottenere i tassi di cambio in formato JSON e verifichiamo che la richiesta sia andata a buon fine, in caso contrario solleviamo un’eccezione:

import requests


def get_json(endpoint):
    response = requests.get(endpoint)

    if response.ok:
        json_data = response.json()
        return json_data
    else:
        print("Status Code: ", response.status_code)
        print("Response Content: ", response.content)
        raise Exception("C'è stato un errore...")


if __name__ == "__main__":
    endpoint = "https://api.exchangeratesapi.io/latest"
    data = get_json(endpoint)
    print(data)

Scriviamo una nuova funzione show_rates() che estragga i dati dal JSON e ci mostri il tasso di conversione da euro a una valuta da noi scelta:

def show_rates(data, currency):
    print("JSON data: ", data)
    rate_date = data["date"]
    exchange_rate = data["rates"][currency]
    print(f"1 EUR corrisponde a {exchange_rate} {currency} il giorno {date}")

Modifichiamo l’istruzione condizionale if __name__ == "__main__" per far sì che chiami la funzione e stampi a schermo le informazioni che ci servono:

if __name__ == "__main__":
    endpoint = "https://api.exchangeratesapi.io/latest"
    data = get_json(endpoint)
    show_rates(data, "SEK")


Creiamo un programma che scarichi i tassi di conversione utilizzando i parametri per connetterci all’endpoint

Ora che abbiamo imparato come effettuare richieste di tipo GET e come gestire i dati che ci vengono inviati come risposta creiamo un nuovo script Python e scriviamo una versione più elegante del programma che abbiamo appena creato.

Vediamo come sia possibile inviare dei parametri con le nostre richieste qualora il servizio con cui stiamo comunicando li supporti, come nel caso di exchangeratesapi, che ci da la possibilità di inviare come parametri le due valute che vogliamo confrontare senza necessariamente dover andare a scaricare tutti i tassi di conversione.

Definiamo una nuova funzione get_rates() che accetti come parametri due valute a e b che tramite GET si colleghi a un endpoint:

import requests


def get_rates():
    response = requests.get("https://api.exchangeratesapi.io/latest?base=USD&symbols=CAD")

I due parametri dell’endpoint base e symbols rappresentano la prima e la seconda valuta da confrontare, in questo caso il Dollaro americano e quello canadese. Modifichiamo la funzione in modo che i parametri vengano aggiunti all’endpoint assegnandoli alla variabile payload:

import requests

def get_rates(a, b):
    
    payload = {"base": a, "symbols": b}
    response = requests.get("https://api.exchangeratesapi.io/latest", params=payload)
    
    if response.ok:
        data = response.json()
        print(data)
        rate_date = data["date"]
        exchange_rate = data["rates"][b]
        print(f"1 {a} corrisponde a {exchange_rate} {b} il giorno {rate_date}")
        
    else:
        print("Status Code: ", response.status_code)
        print("Response Content: ", response.content)
        raise Exception("C'è stato un errore...")


if __name__ == "__main__":
    a = "TRY"
    b = "GBP"
    get_rates(a, b)


Come effettuare richieste di tipo POST tramite requests

Ora che abbiamo imparato come effettuare richieste di tipo GET per ottenere dei dati, vediamo come effettuare richieste di tipo POST per inviare dei dati. Ci collegheremo al server di sviluppo Django dell'applicazione Question Time creata come progetto per il corso Guida per sviluppatori a Django REST Framework e Vue JS.

Scriviamo una funzione login() utilizzando il modulo requests che ci permetta di effettuare il login verso questo servizio ed ottenere come risposta un token di autenticazione che potremo quindi utilizzare per effettuare delle richieste appunto verso gli endpoint messi in sicurezza.

Alla nostra funzione login() passiamo delle credenziali (username e password) per l'autenticazione da inviare ad uno specifico endpoint definito in fase di creazione dell'applicazione stessa. Verifichiamo che la richiesta vada a buon fine e in questo caso scarichiamo il JSON ottenuto come risposta, che conterrà il token:

import requests

def login(credentials):
    response = requests.post("http://127.0.0.1:8000/api/rest-auth/login/", data=credentials)
    
    if response.ok:
        print("Login: Success!")
        print(response.json())
        auth_token = response.json()['key']
        return auth_token
    else:
        raise Exception("Errore. ", response.status_code)
    

if __name__ == "__main__":
    credentials = {"username": "neo", "password": "thereisnospoon"}
    login(credentials)

Se eseguiamo il codice otteniamo il nostro token. Nei casi come questo in cui è necessario comunicare con un API per inviare un token di autenticazione il modo di connettersi all’endpoint cambia a seconda delle specifiche della API, in ogni caso ci si riferisce sempre alla documentazione di quest’ultima.

Nel nostro caso dobbiamo inviare il token di autenticazione come Header, definiamo quindi una nuova funzione che accetta come argomenti un endpoint e un token di autenticazione e facciamo in modo che invii il token utilizzando una f-string tramite una richiesta di tipo GET:

def auth_request(endpoint, auth_token):
    auth_header = f"Token {auth_token}"
    headers = {"Authorization": auth_header}

    response = requests.get(endpoint, headers = headers)
    
    if response.ok:
        response_data = response.json()
        print("Data: ", response_data)
    else:
        raise Exception("Errore. ", response.status_code)

Question Time è sostanzialmente un clone di Quora scritto con Django e fornisce un endpoint che ci permette di ottenere una lista di domande che sono state poste dagli utenti del sito. Aggiungiamolo ad if __name__ == "__main__" e implementiamo la funzione auth_request() che abbiamo appena creato:

if __name__ == "__main__":
    credentials = {"username": "neo", "password": "thereisnospoon"}
    auth_token = login(credentials)
    endpoint = "http://127.0.0.1:8000/api/questions/"
    
    auth_request(endpoint, auth_token)

Se eseguiamo questo script otteniamo una lista di domande con tutti i vari dati associati.


Vuoi imparare Python come un/a professionista? Dai uno sguardo ai nostri