J'exécute actuellement le code suivant basé sur le chapitre 12.5 du livre de recettes Python:
from xml.parsers import expat
class Element(object):
def __init__(self, name, attributes):
self.name = name
self.attributes = attributes
self.cdata = ''
self.children = []
def addChild(self, element):
self.children.append(element)
def getAttribute(self,key):
return self.attributes.get(key)
def getData(self):
return self.cdata
def getElements(self, name=''):
if name:
return [c for c in self.children if c.name == name]
else:
return list(self.children)
class Xml2Obj(object):
def __init__(self):
self.root = None
self.nodeStack = []
def StartElement(self, name, attributes):
element = Element(name.encode(), attributes)
if self.nodeStack:
parent = self.nodeStack[-1]
parent.addChild(element)
else:
self.root = element
self.nodeStack.append(element)
def EndElement(self, name):
self.nodeStack.pop()
def CharacterData(self,data):
if data.strip():
data = data.encode()
element = self.nodeStack[-1]
element.cdata += data
def Parse(self, filename):
Parser = expat.ParserCreate()
Parser.StartElementHandler = self.StartElement
Parser.EndElementHandler = self.EndElement
Parser.CharacterDataHandler = self.CharacterData
ParserStatus = Parser.Parse(open(filename).read(),1)
return self.root
Je travaille avec des documents XML d'environ 1 Go. Quelqu'un connaît-il un moyen plus rapide de les analyser?
J'ai l'impression que vous n'avez pas besoin de capacités DOM de votre programme. J'appuierais l'utilisation de la bibliothèque (c) ElementTree. Si vous utilisez la fonction iterparse du module cElementTree, vous pouvez parcourir le xml et gérer les événements au fur et à mesure qu'ils se produisent.
Notez cependant les conseils de Fredriks sur l'utilisation de cElementTree fonction iterparse :
pour analyser des fichiers volumineux, vous pouvez vous débarrasser des éléments dès que vous les avez traités:
for event, elem in iterparse(source):
if elem.tag == "record":
... process record elements ...
elem.clear()
Le modèle ci-dessus a un inconvénient; il n'efface pas l'élément racine, vous vous retrouverez donc avec un seul élément avec beaucoup d'éléments enfants vides. Si vos fichiers sont volumineux, plutôt que simplement volumineux, cela peut être un problème. Pour contourner ce problème, vous devez mettre la main sur l'élément racine. La façon la plus simple de procéder consiste à activer les événements de démarrage et à enregistrer une référence au premier élément d'une variable:
# get an iterable
context = iterparse(source, events=("start", "end"))
# turn it into an iterator
context = iter(context)
# get the root element
event, root = context.next()
for event, elem in context:
if event == "end" and elem.tag == "record":
... process record elements ...
root.clear()
lxml.iterparse () ne le permet pas.
Le précédent ne fonctionne pas sur Python 3.7, considérez la façon suivante pour obtenir le premier élément.
# get an iterable
context = iterparse(source, events=("start", "end"))
is_first = True
for event, elem in context:
# get the root element
if is_first:
root = Elm
is_first = False
if event == "end" and elem.tag == "record":
... process record elements ...
root.clear()
Avez-vous essayé le module cElementTree?
cElementTree est inclus avec Python 2.5 et versions ultérieures, sous la forme xml.etree.cElementTree. Reportez-vous à benchmarks .
Suppression du lien ImageShack mort
Je vous recommande d'utiliser lxml , c'est une liaison python pour la bibliothèque libxml2 qui est vraiment rapide.
D'après mon expérience, libxml2 et expat ont des performances très similaires. Mais je préfère libxml2 (et lxml pour python) car il semble être développé et testé plus activement. Libxml2 a également plus de fonctionnalités.
lxml est principalement compatible API avec xml.etree.ElementTree . Et il y a une bonne documentation sur son site Web.
L'enregistrement des rappels ralentit considérablement l'analyse. [EDIT] C'est parce que le code C (rapide) doit invoquer l'interpréteur python qui n'est tout simplement pas aussi rapide que C. En gros, vous utilisez le code C pour lire le fichier ( rapide), puis créez le DOM dans Python (lent). [/ EDIT]
Essayez d'utiliser xml.etree.ElementTree qui est implémenté à 100% en C et qui peut analyser XML sans aucun rappel vers python code.
Une fois le document analysé, vous pouvez le filtrer pour obtenir ce que vous voulez.
Si c'est encore trop lent et que vous n'avez pas besoin d'un DOM, une autre option consiste à lire le fichier dans une chaîne et à utiliser des opérations de chaîne simples pour le traiter.
Si votre application est sensible aux performances et susceptible de rencontrer des fichiers volumineux (comme vous l'avez dit,> 1 Go), je vous déconseille d'utiliser fortement contre le code affiché dans votre question pour la simplicité raison il charge tout le document dans la RAM . Je vous encourage à repenser votre conception (si possible) pour éviter de tenir l'arborescence complète du document dans RAM à la fois. Ne sachant pas quelles sont les exigences de votre application, je ne peux pas suggérer correctement approche spécifique, autre que le conseil générique pour essayer d'utiliser une conception "basée sur les événements".
expat ParseFile fonctionne bien si vous n'avez pas besoin de stocker la totalité de l'arborescence en mémoire, ce qui fera tôt ou tard exploser votre RAM pour les fichiers volumineux:
import xml.parsers.expat
parser = xml.parsers.expat.ParserCreate()
parser.ParseFile(open('path.xml', 'r'))
Il lit les fichiers en morceaux et les transmet à l'analyseur sans exploser la RAM.
Doc: https://docs.python.org/2/library/pyexpat.html#xml.parsers.expat.xmlparser.ParseFile
Apparemment PyRXP est vraiment rapide.
Ils prétendent que c'est l'analyseur le plus rapide - mais cElementTree n'est pas dans leur liste de statistiques.
J'ai passé un certain temps à essayer cela et il semble que l'approche la plus rapide et la moins gourmande en mémoire utilise lxml et iterparse, mais en veillant à libérer de la mémoire inutile. Dans mon exemple, analyser le vidage arXiv:
from lxml import etree
context = etree.iterparse('path/to/file', events=('end',), tag='Record')
for event, element in context:
record_id = element.findtext('.//{http://arxiv.org/OAI/arXiv/}id')
created = element.findtext('.//{http://arxiv.org/OAI/arXiv/}created')
print(record_id, created)
# Free memory.
element.clear()
while element.getprevious() is not None:
del element.getparent()[0]
Donc element.clear
ne suffit pas, mais aussi la suppression des liens vers les éléments précédents.