HTTP in Python¶
HTTP is een client-server protocol: de client, bijvoorbeeld de browser, stuurt een verzoek (request) naar de server, die een antwoord (response) terug stuurt. In ons geval is ons Python-programma (in dit notebook) de client.
Met behulp van de requests
library (https://requests.readthedocs.io) kun je in Python HTTP-verzoeken sturen
en de antwoorden verwerken.
Dit is de basis voor het gebruik van web-API’s in Python.
Voordat we de library kunnen gebruiken moeten we deze importeren:
import requests
Het eenvoudigste HTTP-request is get
, met een URL als parameter:
r = requests.get('https://ieni.org')
Aan de hand van de status_code
kun je zien of de request geslaagd is, en een succesvolle response opgeleverd heeft.
De waarde 200
geeft succes aan; 404
is een niet-gevonden pagina; enz.
r.status_code
De response bevat onder meer de headers met metadata van de server.
for header in r.headers:
print(header)
De Content-type
header beschrijft het document-formaat van de response, in dit geval HTML:
r.headers['Content-Type']
We geven hier alleen een klein gedeelte van het ontvangen HTML-document weer:
r.text[0:500]
Andere requests-mogelijkheden¶
Met de requests
-library kun je onder andere:
verzoeken versturen met verschillende HTTP-methods: naast
GET
ookPOST
,PUT
,DELETE
, enz.authenticatie/autorisatiegegevens meegeven (username/password)
de headers die je opstuurt naar de server aanpassen
de parameters in de URL opgeven (
...?key1=value1&key=value...
)de payload (data) opgeven voor een
POST
ofPUT
-request
Hieronder en in de andere notebooks kom je een aantal van deze mogelijkheden tegen.
JSON¶
Zoals uit dit voorbeeld mogelijk blijkt, is HTML niet het meest handige formaat om in een programma te verwerken. De meeste web-API’s gebruiken daarom het JSON-formaat (JavaScript Object Notation, zie https://json.org). JSON wordt veel gebruikt om “objecten” die je in programma’s gebruikt buiten het programma te bewaren, bijvoorbeeld in een document-database (zoals MongoDB of CouchDB); en om objecten te communiceren naar andere programma’s, zoals in web-API’s.
De JSON-notatie lijkt sterk op de Python Dictionary notatie.
Voor het omzetten van een Python Dictionary naar en van JSON gebruiken we de functies dumps
en loads
: string = json.dumps(object)
en object = json.loads(string)
.
Voordat we deze functies kunnen gebruiken moeten we eerst de json
-library importeren.
import json
In het voorbeeld hieronder maken we eerst een Python Dictionary aan:
mytodo = {"text": "Boodschappen supermarkt", "done": 0}
mytodo ["text"]
type(mytodo)
Via json.dumps
zetten we dit om in een string (tekst).
Zo’n tekstformaat kun je zonder problemen communiceren of opslaan op een extern medium: je bent niet afhankelijk van een interne, binaire representatie.
mytodo_string = json.dumps(mytodo)
mytodo_string
Merk op dat deze string-representatie erg lijkt op de oorspronkelijke vorm. Maar het is een gewone string, je kunt er geen velden in selecteren enz.
type(mytodo_string)
Met de functie json.loads
zet je deze JSON stringwaarde om in een Python dictionary:
json.loads(mytodo_string)
Ga na wat het type van het resultaat is van de vorige cel.
Ga na dat je in dit resultaat het veld “done” kunt selecteren.
JSON beperkingen¶
JSON heeft maar een beperkt aantal basis-types: number, string, boolean. Dit betekent dat je complexere basistypes zoals datum als string moet representeren.
Voorbeeld HTTP met JSON¶
In dit voorbeeld gebruiken we een eenvoudige ToDo-toepassing.
Voor deze demonstratie hebben we deze beschikbaar gemaakt op glitch.com
:
https://shadowed-alder.glitch.me/ .
Je kunt de broncode bekijken via: https://glitch.com/edit/#!/shadowed-alder?path=server.js
todo_app = "https://shadowed-alder.glitch.me"
Het volgende verzoek levert een JSON-document op met alle todo’s:
r1 = requests.get(todo_app + "/todos")
r1.status_code
Zoals je ziet is het resultaat (de payload) een text in JSON-formaat:
r1.text
De requests-library heeft een ingebouwde JSON-decoder.
De functie r1.json()
levert de Python-waarde (hier: list van dictionaries) als resultaat van de decodering.
r1.json()
We kunnen de gegevens van een enkele todo opvragen, door de id
hiervan in de URL op te nemen:
r2 = requests.get(todo_app + "/todos/2")
r2.status_code
mytodo = r2.json()
mytodo
PUT: update¶
Met een PUT
-request kunnen we de toestand van deze todo veranderen, bijvoorbeeld done
op 1 zetten.
mytodo['done'] = 1
r3 = requests.put(todo_app + "/todos/2", data=mytodo)
r3.status_code
r3.json()
POST: create¶
Een nieuwe todo kunnen we aanmaken met een POST
naar URL voor de verzameling todo’s.
De id
in het resultaat is de identificatie van de todo in de API.
newtodo = {'text': 'Huiswerk maken!', 'done': 0}
r4 = requests.post(todo_app + "/todos", data=newtodo)
r4.status_code
r4.json()
todo_id = r4.json()["id"]
r5 = requests.get(todo_app + "/todos/{id}".format(
id = todo_id
))
r5.status_code
r5.json()
Delete: delete¶
Met de DELETE
http-method kunnen we een todo ook weer verwijderen.
De gebruiken de id
van de zojuist aangemaakte todo.
r5 = requests.delete(todo_app + "/todos/{id}".format(
id = todo_id
))
r5.status_code
r5.json()
Authenticatie en autorisatie¶
Dit todo-voorbeeld is in één opzicht niet erg praktisch: iedereen kan deze lijst aanpassen. In de praktijk zul je bij zo’n soort toepassing moeten inloggen, met een gebruikersnaam en wachtwoord. Deze combinatie zorgt voor authenticatie: de server weet wie je bent, alleen jij kent deze combinatie (als het goed is ;-). Impliciet zorgt dit ook voor autorisatie: je mag nu gebruik maken van de diensten die aan jouw naam gekoppeld zijn.
authenticatie: ben jij wie je zegt dat je bent? Hoe kun je dat bewijzen?
autorisatie: heb jij hiervoor toestemming?
Bij het gebruik van web-api’s gebruik je vaak een API-key of een token dat de rol van de gebruikersnaam/wachtwoord vervult. Je kunt als aangemelde gebruiker vaak zo’n key aanmaken. Wees erop bedacht dat je deze net zo vertrouwelijk moet behandelen als je gebruikersnaam/wachtwoord-combinatie!
openweathermap¶
Via https://openweathermap.org kun je het actuele weer op een bepaalde lokatie ophalen. Je moet daarvoor een gratis account aanmaken. (Met een betaalde account krijg je meer mogelijkheden.) De beschrijving van de API voor het huidige weer vind je via: https://openweathermap.org/current
In je account kun je een API-key aanmaken die je nodig hebt als authenticatie/autorisatie, zie de tab API-keys (als je ingelogd bent).
openweathermap_key = 'vulhierjekeyin'
url = 'https://api.openweathermap.org/data/2.5/weather?q={city},{country}&appid={API_key}'.format(
city = "Amsterdam",
country = "nl",
API_key = openweathermap_key
)
rw = requests.get(url)
rw.status_code
rw.json()