2008-11-27 20 views
45

Actualmente soy el siguiente código basado en el Capítulo 12.5 del libro de cocina de Python:¿Cuál es la forma más rápida de analizar grandes documentos XML en 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 

estoy trabajando con documentos XML aproximadamente 1 GB de tamaño. ¿Alguien sabe una forma más rápida de analizar estos?

+2

su pregunta es demasiado vaga para recopilar respuestas útiles. Considere responder estas preguntas: - ¿Qué está tratando de hacer con este documento XML de 1GB? - ¿Qué tan rápido necesita este analizador? - ¿Podría iterar de forma perezosa en el documento, en lugar de cargar todo en la memoria desde el principio? – Matt

+2

Necesito cargarlo todo en la memoria, indexar los datos y luego 'navegar' y procesarlo. –

Respuesta

52

Me parece que no necesita ninguna capacidad DOM de su programa. Recomendaría el uso de la biblioteca (c) ElementTree. Si usa la función iterparse del módulo cElementTree, puede abrirse camino a través del xml y tratar los eventos a medida que ocurren.

Obsérvese, sin embargo, el asesoramiento sobre el uso de Fredriks cElementTree iterparse function:

para analizar archivos de gran tamaño, usted puede deshacerse de elementos tan pronto como se haya procesado ellos:

for event, elem in iterparse(source): 
    if elem.tag == "record": 
     ... process record elements ... 
     elem.clear() 

El patrón anterior tiene un inconveniente; no borra el elemento raíz, por lo que terminará con un elemento único con muchos elementos secundarios vacíos. Si sus archivos son enormes, en lugar de grandes, podría ser un problema. Para solucionar esto, debe tener en sus manos el elemento raíz. La forma más sencilla de hacerlo es permitir a los eventos de inicio y guardar una referencia al primer elemento en una 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() 

la lxml.iterparse() no lo permite.

4

El registro de devoluciones de llamadas ralentiza tremendamente el análisis. [EDITAR] Esto se debe a que el código C (rápido) tiene que invocar al intérprete python que simplemente no es tan rápido como C. Básicamente, estás usando el código C para leer el archivo (rápido) y luego compilar el DOM en Python (lento). [/ EDIT]

Trate de usar xml.etree.ElementTree que se implementa al 100% en C y que puede analizar XML sin ninguna devolución de llamada al código python.

Después de analizar el documento, puede filtrarlo para obtener lo que desea.

Si aún es demasiado lento y no necesita un DOM, otra opción es leer el archivo en una cadena y usar operaciones simples de cadena para procesarlo.

+0

Este es un consejo muy engañoso. No hay nada sobre un analizador XML basado en devolución de llamada que sea intrínsecamente lento. Además, el OP ya está usando los enlaces de expansión de Python, que también son nativos C. – Matt

+0

El intérprete de python siempre es más lento que el código de C compilado de forma nativa. Y como puede ver claramente en el código de la pregunta, ¡se está registrando el código de Python para que se llame por cada elemento! ¡Y este código también hace mucho trabajo! –

+0

Esto se debe aumentar, las devoluciones de llamadas en python son realmente lentas, desea evitar eso y hacer tanto como sea posible en C land. –

8

Le recomiendo que use lxml, es un enlace de pitón para la biblioteca libxml2 que es realmente rápido.

En mi experiencia, libxml2 y expat tienen un rendimiento muy similar. Pero prefiero libxml2 (y lxml para python) porque parece ser más activamente desarrollado y probado. También libxml2 tiene más funciones.

lxml es principalmente compatible con API con xml.etree.ElementTree. Y hay buena documentación en su sitio web.

+2

lxml es la regla! :) – ddaa

15

¿Has probado The cElementTree Module?

cElementTree se incluye con Python 2.5 y posterior, como xml.etree.cElementTree. Consulte el benchmarks.

eliminado ImageShack vínculo roto

+0

la imagen no se muestra: ( – fedorqui

4

Si su aplicación es sensible el rendimiento y la probable encontrar archivos de gran tamaño (como usted ha dicho,> 1 GB) entonces yo fuertemente aconsejan no utilizar el código que está mostrando en su pregunta por la simple razón de que carga todo el documento en la RAM. Le recomiendo que reconsidere su diseño (si es posible) para evitar tener todo el árbol de documentos en la memoria RAM de una vez. Sin saber cuáles son los requisitos de su aplicación, no puedo sugerir adecuadamente ningún enfoque específico, que no sea el consejo genérico para tratar de utilizar un diseño "basado en eventos".

0

Aparentemente PyRXP es realmente rápido.

Ellos afirman que es el analizador más rápido, pero cElementTree no está en su lista de estadísticas.

1

parseFile expatriado funciona bien si no es necesario almacenar todo el árbol en la memoria, que tarde o temprano se suene la RAM para archivos de gran tamaño:

import xml.parsers.expat 
parser = xml.parsers.expat.ParserCreate() 
parser.ParseFile(open('path.xml', 'r')) 

Se lee los archivos en trozos, y los alimenta al analizador sintáctico sin explotar la RAM.

Doc: https://docs.python.org/2/library/pyexpat.html#xml.parsers.expat.xmlparser.ParseFile

Cuestiones relacionadas