84 votes

Existe-t-il un moyen rapide et efficace en termes de mémoire pour charger de gros fichiers JSON ?

J'ai des fichiers json de 500 Mo. Si j'utilise la méthode "triviale" json.load() pour charger son contenu en une seule fois, il consommera beaucoup de mémoire.

Existe-t-il un moyen de lire partiellement le fichier ? S'il s'agissait d'un fichier texte, délimité par des lignes, je serais en mesure d'itérer sur les lignes. Je cherche une analogie.

96voto

Jim Pivarski Points 1068

Il y avait un duplicata de cette question qui avait une meilleure réponse. Voir https://stackoverflow.com/a/10382359/1623645 ce qui suggère ijson .

Mise à jour :

Je l'ai essayé, et ijson est à JSON ce que SAX est à XML. Par exemple, vous pouvez faire ça :

import ijson
for prefix, the_type, value in ijson.parse(open(json_file_name)):
    print prefix, the_type, value

donde prefix est un index séparé par des points dans l'arbre JSON (que se passe-t-il si vos noms de clés contiennent des points ? Je suppose que ce serait mauvais pour Javascript, aussi...), theType décrit un événement de type SAX, l'un des 'null', 'boolean', 'number', 'string', 'map_key', 'start_map', 'end_map', 'start_array', 'end_array' y value est la valeur de l'objet ou None si the_type est un événement comme le début/la fin d'une carte/réseau.

Le projet a quelques documentations, mais pas assez de documentation globale. J'ai dû creuser dans ijson/common.py pour trouver ce que je cherchais.

16voto

jcdyer Points 7956

Le problème n'est donc pas que chaque fichier est trop gros, mais qu'il y en a trop, et qu'ils semblent s'accumuler en mémoire. Le ramasseur de déchets de Python devrait suffire, à moins que vous ne conserviez des références dont vous n'avez pas besoin. Il est difficile de dire exactement ce qui se passe sans plus d'informations, mais vous pouvez essayer certaines choses :

  1. Modularisez votre code. Faites quelque chose comme :

    for json_file in list_of_files:
        process_file(json_file)

    Si vous écrivez process_file() de manière à ne pas dépendre d'un état global, et à ne pas ne modifie aucun état global, le ramasseur d'ordures devrait être en mesure de faire son travail.

  2. Traitez chaque fichier dans un processus distinct. Au lieu d'analyser tous les fichiers JSON en même temps, écrivez un programme qui n'en analyse qu'un seul. programme qui n'en analyse qu'un seul, et passez chacun d'eux depuis un script shell, ou depuis un autre processus python qui appelle votre script par l'intermédiaire de subprocess.Popen . C'est un peu moins élégant, mais si rien d'autre ne fonctionne, cela vous permettra de ne pas conserver des données périmées d'un fichier à l'autre. suivant.

J'espère que cela vous aidera.

8voto

kashif Points 93

Oui.

Vous pouvez utiliser jsonstreamer Un analyseur push de type SAX que j'ai écrit et qui vous permettra d'analyser des morceaux de taille arbitraire. Obtenez-le ici et consultez le README pour des exemples. Il est rapide car il utilise la bibliothèque 'C' yajl.

4voto

ak1234 Points 121

Cela peut être fait en utilisant ijson . Le fonctionnement de ijson a été très bien expliqué par Jim Pivarski dans la réponse ci-dessus. Le code ci-dessous va lire un fichier et imprimer chaque json de la liste. Par exemple, le contenu du fichier est le suivant

[{"name": "rantidine",  "drug": {"type": "tablet", "content_type": "solid"}},
{"name": "nicip",  "drug": {"type": "capsule", "content_type": "solid"}}]

Vous pouvez imprimer chaque élément du tableau en utilisant la méthode suivante

 def extract_json(filename):
      with open(filename, 'rb') as input_file:
          jsonobj = ijson.items(input_file, 'item')
          jsons = (o for o in jsonobj)
          for j in jsons:
             print(j)

Note : "item" est le préfixe par défaut donné par ijson.

Si vous voulez accéder uniquement à des fichiers json spécifiques en fonction d'une condition, vous pouvez le faire de la manière suivante.

def extract_tabtype(filename):
    with open(filename, 'rb') as input_file:
        objects = ijson.items(input_file, 'item.drugs')
        tabtype = (o for o in objects if o['type'] == 'tablet')
        for prop in tabtype:
            print(prop)

Cela n'imprimera que les json dont le type est tablet.

3voto

Aea Points 552

En ce qui concerne votre mention de manque de mémoire, je dois me demander si vous gérez réellement la mémoire. Utilisez-vous le mot-clé "del" pour supprimer votre ancien objet avant d'essayer d'en lire un nouveau ? Python ne devrait jamais conserver silencieusement quelque chose en mémoire si vous le supprimez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X