Utilisation de JSONPath pour analyser des données JSON

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')]

Étiquettes: JSON JSONPath Python API extraction donnees

Publié le 14 juillet à 08h59