2009-10-20 30 views
14

estoy usando Python (minidom) para analizar un archivo XML que imprime una estructura jerárquica que se ve algo como esto (la sangría se utiliza aquí para mostrar la relación jerárquica significativo):análisis XML con Python y minidom

My Document 
Overview 
    Basic Features 
    About This Software 
     Platforms Supported 

En su lugar, el programa itera varias veces sobre los nodos y produce lo siguiente, imprimiendo nodos duplicados. (En cuanto a la lista de nodos en cada iteración, es obvio por qué se hace esto pero parece que no puede encontrar una manera de conseguir la lista de nodos que estoy buscando.)

My Document 
Overview 
Basic Features 
About This Software 
Platforms Supported 
Basic Features 
About This Software 
Platforms Supported 
Platforms Supported 

Aquí está el origen XML archivo:

<?xml version="1.0" encoding="UTF-8"?> 
<DOCMAP> 
    <Topic Target="ALL"> 
     <Title>My Document</Title> 
    </Topic> 
    <Topic Target="ALL"> 
     <Title>Overview</Title> 
     <Topic Target="ALL"> 
      <Title>Basic Features</Title> 
     </Topic> 
     <Topic Target="ALL"> 
      <Title>About This Software</Title> 
      <Topic Target="ALL"> 
       <Title>Platforms Supported</Title> 
      </Topic> 
     </Topic> 
    </Topic> 
</DOCMAP> 

Este es el programa de Python:

import xml.dom.minidom 
from xml.dom.minidom import Node 

dom = xml.dom.minidom.parse("test.xml") 
Topic=dom.getElementsByTagName('Topic') 
i = 0 
for node in Topic: 
    alist=node.getElementsByTagName('Title') 
    for a in alist: 
     Title= a.firstChild.data 
     print Title 

pude solucionar el problema no anidan elementos 'tema', cambiando los nombres de los temas de nivel inferior a algo así como 'SubTopic1' y ' SubTopic2 '. Pero, quiero aprovechar la estructuración jerárquica XML incorporada sin necesidad de nombres de elementos diferentes; parece que debería poder anidar elementos 'Tema' y que debería haber alguna manera de saber qué nivel 'Tema' estoy mirando actualmente.

He intentado varias funciones diferentes de XPath sin mucho éxito.

+0

Si desea que la salida de la primera se puede simplemente imprimir el texto de cada elemento - No me queda claro cómo el structuting afecta a la salida deseada – Mark

Respuesta

8

getElementsByTagName es recursivo, obtendrá todos los descendientes con un tagName coincidente. Como sus Temas contienen otros Temas que también tienen Títulos, la llamada obtendrá los Títulos de más abajo muchas veces.

Si usted quiera pedir sólo todos los niños directos a juego, y usted no tiene XPath disponible, puede escribir un filtro simple, por ejemplo .:

def getChildrenByTagName(node, tagName): 
    for child in node.childNodes: 
     if child.nodeType==child.ELEMENT_NODE and (tagName=='*' or child.tagName==tagName): 
      yield child 

for topic in document.getElementsByTagName('Topic'): 
    title= list(getChildrenByTagName('Title'))[0]   # or just get(...).next() 
    print title.firstChild.data 
+0

Gracias por el intento. No funcionó, pero me dio algunas ideas. Las siguientes obras (la misma idea general; Fwiw, la nodeType es ELEMENT_NODE): importación xml.dom.minidom desde el Nodo importación xml.dom.minidom dom = xml.dom.minidom.parse ("docmap.xml ") def getChildrenByTitle (nodo): para niño en node.childNodes: si 'Título' child.localName ==: rendimiento niño Tema = dom.getElementsByTagName ('Tema') para el nodo en el Tema: alist = getChildrenByTitle (nodo) para un alistado: # Título = a.firstChild.data Título = a.childNodes [0] .nodeValue print Título – hWorks

+0

Vaya, sí, quise decir que ELEMENT no es TEXTO, por supuesto! doh, corregido – bobince

7

me deja poner ese comentario aquí ..

Gracias por el intento. No funcionó, pero me dio algunas ideas. Las siguientes obras (la misma idea general; Fwiw, la nodeType es ELEMENT_NODE):

import xml.dom.minidom 
from xml.dom.minidom import Node 

dom = xml.dom.minidom.parse("docmap.xml") 

def getChildrenByTitle(node): 
    for child in node.childNodes: 
     if child.localName=='Title': 
      yield child 

Topic=dom.getElementsByTagName('Topic') 
for node in Topic: 
    alist=getChildrenByTitle(node) 
    for a in alist: 
#  Title= a.firstChild.data 
     Title= a.childNodes[0].nodeValue 
     print Title 
+0

Llamaría a la función getTitle (o 'get_title'), y no devolvería todos los elementos de título secundarios inmediatos, sino solo el primero (ya que de todos modos debería haber solo un título por niño). –

+0

Quizás esto es lo que no obtendré. Quiero los títulos de todos los niños inmediatos. Tal vez un nombre mejor sería getTitlesOfChildren. – hWorks

3

podría utilizar el siguiente generador para correr por la lista y obtener títulos con niveles de sangría:

def f(elem, level=-1): 
    if elem.nodeName == "Title": 
     yield elem.childNodes[0].nodeValue, level 
    elif elem.nodeType == elem.ELEMENT_NODE: 
     for child in elem.childNodes: 
      for e, l in f(child, level + 1): 
       yield e, l 

Si se haga la prueba con el archivo:

import xml.dom.minidom as minidom 
doc = minidom.parse("test.xml") 
list(f(doc)) 

obtendrá una lista con las siguientes tuplas:

(u'My Document', 1), 
(u'Overview', 1), 
(u'Basic Features', 2), 
(u'About This Software', 2), 
(u'Platforms Supported', 3) 

Es solo una idea básica para ser afinado, por supuesto. Si solo quiere espacios al principio, puede codificarlos directamente en el generador, aunque con el nivel tiene más flexibilidad. También podría detectar el primer nivel de forma automática (aquí no es nada fácil inicializar el nivel en -1 ...).

+0

Exactamente lo que he intentado hacer todo el día antes de encontrar generadores. Muchas gracias. – hWorks

1

función Recusive:

import xml.dom.minidom 

def traverseTree(document, depth=0): 
    tag = document.tagName 
    for child in document.childNodes: 
    if child.nodeType == child.TEXT_NODE: 
     if document.tagName == 'Title': 
     print depth*' ', child.data 
    if child.nodeType == xml.dom.Node.ELEMENT_NODE: 
     traverseTree(child, depth+1) 

filename = 'sample.xml' 
dom = xml.dom.minidom.parse(filename) 
traverseTree(dom.documentElement) 

Su xml:

<?xml version="1.0" encoding="UTF-8"?> 
<DOCMAP> 
    <Topic Target="ALL"> 
     <Title>My Document</Title> 
    </Topic> 
    <Topic Target="ALL"> 
     <Title>Overview</Title> 
     <Topic Target="ALL"> 
      <Title>Basic Features</Title> 
     </Topic> 
     <Topic Target="ALL"> 
      <Title>About This Software</Title> 
      <Topic Target="ALL"> 
       <Title>Platforms Supported</Title> 
      </Topic> 
     </Topic> 
    </Topic> 
</DOCMAP> 

Su salida deseada:

$ python parse_sample.py 
     My Document 
     Overview 
      Basic Features 
      About This Software 
       Platforms Supported 
2

creo que puede ayudar

import os 
import sys 
import subprocess 
import base64,xml.dom.minidom 
from xml.dom.minidom import Node 
f = open("file.xml",'r') 
data = f.read() 
i = 0 
doc = xml.dom.minidom.parseString(data) 
for topic in doc.getElementsByTagName('Topic'): 
    title= doc.getElementsByTagName('Title')[i].firstChild.nodeValue 
    print title 
    i +=1 

Salida:

My Document 
Overview 
Basic Features 
About This Software 
Platforms Supported