2010-08-29 41 views
8

Me quedé atrapado con XML y Python. La tarea es simple, pero no pude resolverla hasta el momento y la pasé en ese largo tiempo. Vine aquí por un consejo sobre cómo resolverlo con un par de líneas.Cómo eliminar elementos de XML usando Python

Gracias por cualquier ayuda para atravesar el árbol. Siempre terminé con demasiados o muy pocos elementos. Los elementos se pueden anidar sin límite. El ejemplo dado es solo un ejemplo. Voy a aceptar cualquier solución, no exigente con dom, minidom, saxofón, cualquiera que sea ..

que tienen un archivo XML similar a éste:

<root> 
    <elm> 
     <elm>Common content</elm> 

     <elm xmlns="http://example.org/ns"> 
      <elm lang="en">Content EN</elm> 
      <elm lang="cs">žluťoučký koníček</elm> 
     </elm> 

     <elm xml:id="abc123">Common content</elm> 

     <elm lang="en">Content EN</elm> 
     <elm lang="cs">Content CS</elm> 

     <elm lang="en"> 
      <elm>Content EN</elm> 
      <elm>Content EN</elm> 
     </elm> 

     <elm lang="cs"> 
      <elm>Content CS</elm> 
      <elm>Content CS</elm> 
     </elm> 
    </elm> 
</root> 

Lo que necesito - analizar el XML y escribir un nuevo archivo. El nuevo archivo debe contener todos los elementos para el lenguaje y los elementos dados sin el atributo lang.

Para el idioma "CS" el archivo de salida debe containt esto:

<root> 
    <elm> 
     <elm>Common content</elm> 

     <elm xmlns="http://example.org/ns"> 
      <elm lang="cs">žluťoučký koníček</elm> 
     </elm> 

     <elm xml:id="abc123">Common content</elm> 

     <elm lang="cs">Content CS</elm> 

     <elm lang="cs"> 
      <elm>Content CS</elm> 
      <elm>Content CS</elm> 
     </elm> 
    </elm> 
</root> 

Si usted puede hacer que omitir el atributo lang en el nuevo archivo, aún mejor. Pero no es tan importante.

ACTUALIZACIÓN1: Se agregaron caracteres unicode y el atributo de espacio de nombres.

UPDATE2: Utilizando Python 2.5, se prefieren las bibliotecas estándar.

+0

'Para el idioma" en ", el archivo de salida debe contener esto:' ¿Supongo que quiso decir que la salida dada es para el lenguaje "cs"? – LarsH

+0

@LarsH: Actualicé la pregunta para agregar algunos caracteres Unicode allí. Tienes razón, debe haber escrito: para el lenguaje "cs". Lo cambiará – dwich

Respuesta

10

Usando lxml:

import lxml.etree as le 

with open('doc.xml','r') as f: 
    doc=le.parse(f) 
    for elem in doc.xpath('//*[attribute::lang]'): 
     if elem.attrib['lang']=='en': 
      elem.attrib.pop('lang') 
     else: 
      parent=elem.getparent() 
      parent.remove(elem) 
    print(le.tostring(doc)) 

rendimientos

<root> 
    <elm>Common content</elm> 

    <elm> 
     <elm>Content EN</elm> 
     </elm> 

    <elm>Common content</elm> 

    <elm>Content EN</elm> 
    <elm> 
     <elm>Content EN</elm> 
     <elm>Content EN</elm> 
    </elm> 

    </root> 
+0

Muchas gracias. No puedo instalar lxml en mi WinXP, problema con el compilador. Lo probaré más tarde. – dwich

+0

¡Funciona! ¡Gracias! Me salvaste la noche :) Les agradezco a los dos, ambas soluciones son buenas. – dwich

+0

Me alegro de poder ayudar :) – unutbu

5

no estoy seguro de cuál es la mejor para quitar el atributo lang, pero aquí hay un código que hace los otros cambios (Python 2.7; para 2.5 o 2.6, utilice getIterator en lugar de iter), suponiendo que cuando se quita un elemento que también siempre quiere eliminar todo lo contenido en ese elemento.

Este código sólo imprime el resultado a la salida estándar (se puede redirigirlo como desee, por supuesto, o escribirlos directamente a un nuevo archivo, y así sucesivamente):

import sys 
from xml.etree import cElementTree as et 

def picklang(path, lang='en'): 
    tr = et.parse(path) 
    for element in tr.iter(): 
     for subelement in element: 
      la = subelement.get('lang') 
      if la is not None and la != lang: 
       element.remove(subelement) 
    return tr 

if __name__ == '__main__': 
    tr = picklang('la.xml') 
    tr.write(sys.stdout) 
    print 

Con la.xml ser su ejemplo, esto escribe

<root> 
    <elm>Common content</elm> 

    <elm> 
     <elm lang="en">Content EN</elm> 
     </elm> 

    <elm>Common content</elm> 

    <elm lang="en">Content EN</elm> 
    <elm lang="en"> 
     <elm>Content EN</elm> 
     <elm>Content EN</elm> 
    </elm> 

    </root> 
+0

Gracias Alex, funciona genial. Excepto dos cosas: espacio de nombres y unicode. Si hay un atributo xmlns, por ejemplo '', el nuevo nodo obtiene un 'xmlns: ns0 =" http://example.org/ns "' atributo y todos los nodos secundarios obtienen un prefijo ' dwich

+0

@dwich, para la escritura puede simplemente agregar a la llamada 'write' un parámetro' encoding' de su elección. La estética como el tema del espacio de nombres (que creo que no cambia la semántica del XML) es mucho más difícil de tratar, por desgracia (al igual que, por ejemplo, puede haber notado, la sangría en la salida es diferente, porque los espacios en blanco los elementos que se eliminan también desaparecen). –

+0

Esa cosa Unicode fue mi error, comencé a jugar con códecs y aunque utilicé 'encoding = 'utf-8'', no funcionó (porque no lo abrí de forma incorrecta). Gracias por su respuesta, elegiré la solución de ~ unutbu ya que su código no tiene problemas con el espacio de nombres. Ambas respuestas son correctas. ¡Gracias chicos! – dwich

1

Actualización del código de @Alex Martelli para eliminar un error en la lista de elementos se actualiza en su lugar. La solución anterior dará una respuesta incorrecta si la entrada es un poco más compleja.

Código
import sys 
from xml.etree import cElementTree as et 

def picklang(path, lang='en'): 
    tr = et.parse(path) 
    for element in tr.iter(): 
     for subelement in element[:]: 
      la = subelement.get('lang') 

      if la is not None and la != lang: 
       element.remove(subelement) 
    return tr 

if __name__ == '__main__': 
    tr = picklang('la.xml') 
    tr.write(sys.stdout) 
    print 

en la línea 7 for subelement in element: se cambia a for subelement in element[:]: ya que no es correcta a la lista de actualizar en su lugar mientras interactuando sobre ella.

Este código repite una copia de la lista de elementos y elimina elementos cuando lang! = "En" en la lista de elementos originales.

Cuestiones relacionadas