Analyse de JSON avec JSONPath
Lorsque vous disposez d'une chaîne de caractères JSON et que vous souhaitez en extraire certaines parties, il faut normally parcourir toute la structure en effectuant des vérifications à chaque niveau. Une alternative intéressante consiste à utiliser JSONPath, un langage de requêtes similaire à XPath mais pour le format JSON.
Installation
$ pip install jsonpath
Documentation officielle : https://goessner.net/articles/JsonPath/
Exemples et syntaxe disponibles sur : https://github.com/json-path/JsonPath
Référence pour l'implémentatoin Java : https://github.com/alibaba/fastjson/wiki/JSONPath
Comparaison XPath et JSONPath
| XPath | JSONPath | Description |
|---|---|---|
| / | $ | Noeud racine |
| . | @ | Noeud courant |
| / | . or [] | Noeud enfant |
| .. | non supporté | Noeud parent |
| // | .. | N'importe quel niveau descendants |
| * | * | Caractère générique, correpsond à tous les noeuds |
| @ | non supporté | Attribut |
| [] | [] | Indice de tableau |
| | | [,] | XPath utilise OR. JSONPath utilise l'union pour les noms et indices |
| non supporté | [start:end:step] | Tranche |
| [] | ?() | Critères de filtrage |
| non supporté | () | Expression |
| () | non supporté | Regroupement |
Exemple de document JSON
{
"boutique": {
"livre": [
{
"categorie": "reference",
"auteur": "Nigel Rees",
"titre": "Sayings of the Century",
"prix": 8.95
},
{
"categorie": "fiction",
"auteur": "Evelyn Waugh",
"titre": "Sword of Honour",
"prix": 12.99
},
{
"categorie": "fiction",
"auteur": "Herman Melville",
"titre": "Moby Dick",
"isbn": "0-553-21311-3",
"prix": 8.99
},
{
"categorie": "fiction",
"auteur": "J. R. R. Tolkien",
"titre": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"prix": 22.99
}
],
"velo": {
"couleur": "rouge",
"prix": 19.95
}
}
}
| XPath | JSONPath | Description |
|---|---|---|
| /store/book/author | $.boutique.livre[*].auteur | Tous les auteurs des livres |
| //author | $..auteur | Tous les auteurs |
| /store/* | $.boutique.* | Tous les noeuds sous racine.boutique |
| /store//price | $.boutique..prix | Tous les prix dans boutique |
| //book[3] | $..livre[2] | Le troisième livre |
| //book[last()] | $..livre[(@.length-1)] $..livre[-1:] | Dernier livre dans l'ordre |
| //book[position()<3] | $..livre[0,1] $..livre[:2] | Les deux premiers livres |
| //book[isbn] | $..livre[?(@.isbn)] | Livres ayant un ISBN |
| //book[price<10] | $..livre[?(@.prix<10)] | Livres avec prix inférieur à 10 |
| //* | $..* | Tous les noeuds à tous les niveaux |
Exemple pratique : API des films populaires de Douban
URL de l'API : https://movie.douban.com/j/search_subjects?type=movie&tag=热门&page_limit=10&page_start=0
Extraction des films avec une note supérieure à 8 :
from jsonpath import jsonpath
import requests
import json
url = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_limit=10&page_start=0"
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
with requests.get(url=url, headers={'user-agent': user_agent}) as response:
print(response.status_code)
data = json.loads(response.text)
# Tous les titres
print(jsonpath(data, '$..title'))
# Titres et notes pour rate > 8
print(jsonpath(data, '$..subjects[?(@.rate>"8")].title,rate'))
# Résultat :
# 200
# ['Ne regardez pas en haut', 'Jai Bhim', 'Les Chevaliers de Sidonia', '温柔酒吧', 'Messe', 'Matrix Resurrections', 'Encanto', 'The French Dispatch', 'Dune', 'Les Héros du Rail']
# ['Jai Bhim', '8.7', 'Les Chevaliers de Sidonia', '8.5']
# Données sous forme de liste plate
elements = ['Titre1', '8.1', 'Titre2', '7.9', 'Titre3', '8.2', 'Titre4', '7.1']
# Transformation en liste de tuples
resultat = [(elements[i], elements[i + 1]) for i in range(0, len(elements), 2)]
print(resultat)
# Résultat :
# [('Titre1', '8.1'), ('Titre2', '7.9'), ('Titre3', '8.2'), ('Titre4', '7.1')]
Version optimisée avec gestion de fichiers
import json
from jsonpath import jsonpath
import requests
def telecharger_donnees(url_api):
en_tete = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
}
with requests.get(url=url_api, headers=en_tete) as reponse:
if reponse.status_code == 200:
with open("donnees.json", "wb") as fichier:
fichier.write(reponse.content)
def analyser_films():
with open("donnees.json", "r", encoding="utf8") as fichier:
contenu = json.loads(fichier.read())
resultat = jsonpath(contenu, "$..subjects[?(@.rate>'8')].title,rate")
if resultat:
print([(resultat[i], resultat[i + 1]) for i in range(0, len(resultat), 2)])
if __name__ == '__main__':
url_api = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_limit=10&page_start=0"
# telecharger_donnees(url_api)
analyser_films()
# Résultat :
# [('Babylon', '8.0'), ('Aftersun', '8.1'), ('Le Poisson', '8.2')]