In Geautomatiseerd gegevens downloaden heb je geleerd hoe je met requests
data kunt ophalen van een URL. In Een website scrapen met Beautiful Soup leerde je hoe je gegevens van een website zelf kunt verwerken met behulp van Beautiful Soup. In het eerste geval kwam de data als tekstbestand, in het tweede geval als HTML. Maar wat als de data die je wilt ophalen in JSON-formaat is? In deze tutorial leer je hoe je met JSON-data kunt werken in Python.
Het project
Het Centraal Bureau voor de Statistiek (CBS) maakt alle gegevens niet alleen via de website beschikbaar, maar ook via een API (https://opendata.cbs.nl/statline/portal.html). In dit project download je de bevolkingsgegevens, sla je ze op in data.json
en verwerk je ze vervolgens om per jaar het totale bevolkingsaantal van Nederland te tonen.
output
1950: 10.026.773
1951: 10.200.280
...
2022: 17.590.672
2023: 17.811.291
Voordat je begint
Maak een nieuw project aan, met een nieuwe virtual environment. Installeer daarin de benodigde packages:
shell
cd projects
mkdir cbs_json && cd cbs_json
python -m venv .venv
source .venv/bin/activate # .venv\Scripts\activate.bat voor Windows
python -m pip install requests
python -m pip freeze > requirements.txt
Alle code van dit project is ook op Github te bekijken. Omdat deze respository in meerdere tutorials wordt gebruikt, zijn er meerdere branches. Zorg dat je voor deze tutorial versie-1 kiest.
Wat is JSON?
JSON staat voor JavaScript Object Notation. Het is een lichtgewicht dataformaat dat makkelijk voor mensen te lezen en schrijven is en makkelijk voor machines om te genereren en analyseren. Het wordt veel gebruikt om gegevens tussen programma's uit te wisselen, met name via internet. Veel API's zullen dan ook JSON teruggeven.
In onderstaand voorbeeld van de Wikipediapagina over JSON, zie je een lijst met twee elementen:
json
[
{
"Naam":"JSON",
"Type":"Gegevensuitwisselingsformaat",
"isProgrammeertaal":false,
"Zie ook":[
"XML",
"ASN.1"
]
},
{
"Naam":"JavaScript",
"Type":"Programmeertaal",
"isProgrammeertaal":true,
"Jaar":1995
}
]
Zoals je ziet, lijkt een JSON-object erg op een dict
in Python. Net als een dict
wordt een JSON-object omsloten door curly braces ({}
) en bestaat het uit sleutel-waarden paren. Er zijn wel een aantal beperkingen. Als waarde zijn alleen de volgende datatypes toegestaan:
- Tekst
- Getallen
true
, false
en null
- Lijsten (arrays,
[]
)
- Andere JSON-objecten (
{}
)
De sleutels zijn altijd tekst.
JSON in Python
Python heeft een ingebouwde bibliotheek genaamd json
voor het werken met JSON-data. Er zijn twee manieren om dit te doen: van en naar strings en van en naar bestanden:
- Van Python naar een JSON-string:
json.dumps()
- Van Python naar een JSON-bestand:
json.dump()
- Van een JSON-string naar Python:
json.loads()
- Van een JSON-bestand naar Python:
json.load()
Van Python naar JSON noem je serialiseren. Van JSON naar Python noem je deserialiseren.
Verder met het project
Nu je de basis van JSON begrijpt, kun je verder met het project.
Gegevens downloaden
Stap 1 is het downloaden van de gegevens. Doe dit met requests
. Maak in main.py
een functie aan om de gegevens te downloaden. Deze functie is vrijwel gelijk aan de get_html
functie uit Een website scrapen met Beautiful Soup.
main.py
import json
import requests
URL = "https://opendata.cbs.nl/ODataApi/odata/85496NED/TypedDataSet"
def get_data(refresh=True):
if not refresh:
try:
with open("data.json") as f:
data = json.load(f)
except FileNotFoundError:
print("Bestand niet gevonden, downloaden")
# Roep deze functie aan, met `refresh=True`
return get_data(refresh=True)
else:
response = requests.get(URL)
# Als de HTTP-code 400 of hoger is, zal er een uitzondering worden opgeworpen
response.raise_for_status()
data = response.json()
# Sla bestand op
with open("data.json", "w") as f:
json.dump(data, f, indent=2)
return data
De functie zorgt ervoor dat je het bestand opslaat als data.json
, zodat je tijdens het ontwikkelen niet steeds de URL hoeft aan te roepen. Wil je de gegevens verversen, roep dan get_data
aan met refresh=True
.
De gearceerde regels laten zien je waar je in de code met json
werkt. Als je data.json
wilt inladen, gebruik je json.load()
. Haal je de gegevens eerst op van het CBS met requests
, dan gebruik je data = response.json()
. Dit is een hulpmiddel van requests
om inkomende json
om te zetten naar een Python-object. Sla je het bestand vervolgens op, dan gebruik je json.dump(data, f, indent=2)
. Dus: je serialiseert het Python-object data
naar bestand f
. Om het leesbaar te maken gebruik je een indentatie van 2.
Voor de oplettende lezer: met data = response.json()
zet je JSON om naar een dict
, en verderop zet je die dict
weer om naar json met json.dump()
om het naar het bestand te schrijven. Dat is wat omslachtig natuurlijk! Een kortere manier is om de json direct naar het bestand te schrijven, en dat kan ook.
In dit geval wilde ik graag de json.dump()
en de response.json()
methodes laten zien.
Roep de functie eens aan en inspecteer het opgeslagen bestand:
main.py
# ...
if __name__ == "__main__":
data = get_data()
Het resultaat ziet er ongeveer zo uit:
Alle gegevens zijn te vinden in de sleutel value
, welke een lijst bevat met JSON-objecten. Elk jaar is één object. Voor dit project heb je uit die objecten alleen de sleutels Perioden
en TotaleBevolking_1
nodig.
Gegevens verwerken
Nu je de gegevens in data.json
beschikbaar hebt, kun je datgene eruit halen wat je nodig hebt. Maak een functie get_total_population
aan in main.py
.
main.py
# ...
def get_total_population():
# Open het bestand
with open("data.json") as f:
data = json.load(f) # data is nu een Python object (dict)
# Bereid een lege lijst voor
result = []
# Loop door de rijen in de data
for item in data["value"]:
year = item["Perioden"].split("JJ")[0] # Schoon het jaartal op
value = item["TotaleBevolking_1"]
# Voeg het jaartal en de waarde toe aan de lijst als een tuple
result.append((year, value))
return result
Je begint met het inladen van het JSON-bestand, wat zal resulteren in een dict
. Vervolgens loop je over alle items in de data["value"]
lijst om er het jaar (Perioden
) en de bevolkingsomvang (TotaleBevolking_1
) uit te halen. Schoon het jaar op, zodat je in plaats van "1950JJ00" netjes "1950" krijgt.
Roep het geheel tot slot aan en druk de resultaten af:
main.py
import locale
locale.setlocale(locale.LC_ALL, "nl_NL.utf8")
# ...
if __name__ == "__main__":
data = get_data(refresh=False) # Zet op False om niet steeds te downloaden
total_population = get_total_population()
for year, value in total_population:
print(f"{year}: {value:n}")
Bovenaan je bestand stel je locale
in, zodat je op de laatste regel {value:n}
kunt gebruiken om duizendtallen netjes te scheiden door een punt.
Het resultaat:
Conclusie
Werken met JSON in Python is eenvoudig te doen met de json
package, beschikbaar in de standaard bibliotheek. Het omzetten van Python-objecten naar JSON doe je met dump
of dumps
. Het omzetten van JSON naar Python doe je met load
of loads
. Vaak zul je JSON ophalen via een API. Gebruik je hiervoor requests
, dan kun je response.json()
gebruiken om de inkomende data te converteren naar een Python-object.
Alle code van dit project is ook op Github te bekijken. Omdat deze respository in meerdere tutorials wordt gebruikt, zijn er meerdere branches. Zorg dat je voor deze tutorial versie-1 kiest.