Comment économiser de la mémoire lors de la lecture d'énormes XML de plusieurs Go ou plus en Python

introduction

Cela fait longtemps que JSON n'est pas devenu le courant dominant lors de l'échange de données lisibles par machine, mais les données sont parfois distribuées en XML (comme les données publiées par une ancienne institution). Ou si vous effectuez un traitement en langage naturel, par exemple, l'analyseur de syntaxe CaboCha a une option ( -f 3) pour afficher le résultat de l'analyse au format XML, de sorte que le traitement du résultat est dans le format dit "treillis". Je pense que cela peut être utilisé dans le sens où ce sera plus facile.

Dans ce dernier cas, j'essayais de déposer le résultat de l'analyse syntaxique d'un gros corpus en XML, mais lorsque j'ai essayé de traiter le XML de 8 Go sur la machine avec 64 Go de mémoire à portée de main, la mémoire était pleine. Je suis resté coincé au milieu (je ne crache même pas d'erreur). J'ai été un peu surpris car je l'ai fait 64 Go avec l'intention de faire de mon mieux pour augmenter la mémoire.

Le XML en question se présente sous la forme d'une liste avec un certain nombre de balises «» suspendues sous la balise «». Il semble que ce soit aussi un format d'enregistrement.

<root>
    <item>...</item>
    <item>...</item>
    ...
    <item>...</item>
</root>

Lors du traitement de chaque «élément», cela n'a rien à voir avec les autres «éléments», et il est bon de les regarder un par un. Beaucoup d'entre vous savent que l'utilisation de ʻiterator (générateur) est conviviale pour la mémoire lorsque ce type de données est énorme. Bien sûr, les bibliothèques qui gèrent XML ont aussi des méthodes qui permettent de lire les fichiers XML avec ʻiterator, mais c'était un peu délicat.

XML dans la bibliothèque standard Python

Il est facile d'utiliser le standard xml.etree.ElementTree lorsque vous travaillez avec XML en Python. Il existe aussi un fameux dokoro BeautifulSoup, mais il est analysé avec XML que je souhaite manipuler car il est spécialisé en HTML. Il y a une partie qui provoque une erreur [^ 1], et j'en suis accro, donc j'ai choisi la bibliothèque standard. Cet article décrit les précautions à prendre lors de l'exécution de l'analyse XML ʻiteratoravec cette bibliothèque standardxml`.

Utilisation normale (tout mettre en mémoire)

C'est le cas lorsque vous l'utilisez normalement sans utiliser ʻiterator`.

import xml.etree.ElementTree as ET

tree = ET.parse('path/to/xml')

for item in tree.iterfind('item'):
    # do something on item

Vous lisez la balise <item> dans l'arborescence XML avec .iterfind () while ʻiterator. Mais juste avant cela, ʻET.parse () ʻest comme file.readlines () `. Je mange beaucoup de mémoire.

Quand iter (mais mange de la mémoire)

C'est à ce moment que vous voulez lire tout en ʻiter`.

import xml.etree.ElementTree as ET

context = ET.iterparse('path/to/xml')

for event, elem in context:
    if elem.tag == 'item':
        # do something on item

Si ʻET.parse () ʻest changé en ʻET.iterparse () , le XML dans le chemin d'argument sera lu au format ʻiterator. Je l'ai lu tag par tag, mais context renvoie ʻevent et ʻelem uniquement lorsqu'il atteint la fin de la balise. ʻEvent == "end" et ʻelem est un élément.

Maintenant, vous pouvez économiser de la mémoire! Si vous y réfléchissez, c'est une grosse erreur. En fait, même si # faire quelque chose sur l'élément est pass, il utilise autant de mémoire que ** "usage habituel" **.

** ʻiter, mais context` enregistre toutes les balises que vous avez lues jusqu'à présent **.

Quelque part, une variable locale appelée context.root est cachée à l'intérieur de l'itérateur. Je ne le savais pas car je ne l'ai même pas écrit dans la documentation officielle. Peut-être que certaines personnes sont heureuses dans le sens où elles peuvent être consultées à plusieurs reprises plus tard, contrairement au "générateur" habituel. Eh bien, je peux imaginer qu'un tel mécanisme est nécessaire pour lire et conserver la structure imbriquée de XML.

Quand iter (ne pas manger de mémoire)

Alors, que dois-je faire? Conseils sur la page officielle avant qu'elle ne soit incorporée dans le standard de Python 2.5 en tant que bibliothèque nommée ʻElementTree` il y a longtemps. eu. Python était un nouveau venu de 3 donc je ne l'ai pas fait du tout.

import xml.etree.ElementTree as ET

context = ET.iterparse('path/to/xml', events=('start', 'end'))

_, root = next(context)  #Allez un peu plus loin et obtenez root

for event, elem in context:
    if event == 'end' and elem.tag == 'item':
        # do something on item
        root.clear()  #Vider la racine lorsque vous avez terminé

Vous pouvez spécifier l'argument mot-clé ʻeventsdans ʻET.iterparse (), et si vous spécifiez 'start' à ceci, il vous indiquera la balise d'ouverture. La première balise ouverte est «», donc enregistrez-la pour la variable. À ce stade, la valeur ignorée par «_» contient la chaîne de caractères «démarrer».

Si vous prenez root [^ 2], vous pouvez supprimer les informations d'élément de la mémoire par` .clear () ʻà chaque fois. Je suis heureux.


[^ 1]: Si une seule balise telle que «» réservée en HTML est utilisée en XML, même s'il y a du texte à l'intérieur, elle sera effacée. Il y avait probablement une solution de contournement, mais je me souviens que cela n'a pas fonctionné.

[^ 2]: Cela ressemble à Android il y a longtemps et c'est merveilleux.

Recommended Posts

Comment économiser de la mémoire lors de la lecture d'énormes XML de plusieurs Go ou plus en Python
[Python] Extrayez des données texte à partir de données XML de 10 Go ou plus.
Comment vérifier la taille de la mémoire d'une variable en Python
Comment vérifier la taille de la mémoire d'un dictionnaire en Python
Comment bien formater une liste de dictionnaires (ou d'instances) en Python
Résumé de la façon d'importer des fichiers dans Python 3
Comment implémenter la mémoire partagée en Python (mmap.mmap)
Résumé de l'utilisation de MNIST avec Python
Comment obtenir le nombre de chiffres en Python
Comment mesurer le temps de traitement avec Python ou Java
Comment quitter lors de l'utilisation de Python dans Terminal (Mac)
Comment développer dans un environnement virtuel Python [Memo]
Comparaison de l'utilisation des fonctions d'ordre supérieur dans Python 2 et 3
Comment obtenir une liste d'exceptions intégrées pour python
Comment développer en Python
Mettre le processus en veille pendant un certain temps (secondes) ou plus en Python
Comment ne pas échapper au japonais en traitant avec JSON en Python
Comment déterminer l'existence d'un élément sélénium en Python
Comment connaître la structure interne d'un objet en Python
Comment transformer une chaîne en tableau ou un tableau en chaîne en Python
Comment éviter la duplication des données lors de la saisie de Python vers SQLite.
[Note du débutant] Comment spécifier le chemin de lecture de la bibliothèque en Python
[Python] Comment faire PCA avec Python
Comment collecter des images en Python
Comment utiliser SQLite en Python
Comment utiliser Mysql avec python
Comment envelopper C en Python
Comment utiliser ChemSpider en Python
Comment utiliser PubChem avec Python
Comment gérer le japonais avec Python
Comment résoudre l'erreur "Aucun noyau de grammaire Python trouvé" dans Atom
Comment masquer l'invite de commande lors de l'exécution de python dans Visual Studio 2015
Comment envoyer une image visualisée des données créées en Python à Typetalk
[Python] Comment mettre n'importe quel nombre d'entrées standard dans la liste
Comment gérer l'erreur SSL lors de la connexion à S3 avec Python boto
Comment écrire une chaîne de caractères lorsqu'il y a plusieurs lignes en python
[Python] Comment ouvrir deux fichiers ou plus en même temps
[Python] Résumé de l'utilisation des pandas
[Introduction à Python] Comment utiliser la classe en Python?
Comment définir dynamiquement des variables en Python
Comment faire R chartr () en Python
[Itertools.permutations] Comment créer une séquence en Python
Comment utiliser BigQuery en Python
Comment obtenir stacktrace en python
Comment afficher la table quatre-vingt-dix-neuf en python
Comment extraire une zone de polygone en Python
Lors de l'examen de l'utilisation de la mémoire dans Python 3
Comment vérifier la version d'opencv avec python
[Python2.7] Résumé de l'utilisation d'unittest
Comment changer de version de Python dans cloud9
Comment régler le contraste de l'image en Python
Comment utiliser __slots__ dans la classe Python
Comment remplir dynamiquement des zéros avec Python
Utilisation d'opérateurs non logiques de ou en python
Résumé de l'utilisation de la liste Python
Comment utiliser les expressions régulières en Python
[Python2.7] Résumé de l'utilisation du sous-processus
Comment afficher Hello World en python
Comment utiliser is et == en Python