2009-10-20 22 views
14

sto usando Python (minidom) per analizzare un file XML che consente di stampare una struttura gerarchica che sembra qualcosa di simile (indentazione viene utilizzato qui per mostrare la relazione gerarchica significativo):analisi XML con Python e minidom

My Document 
Overview 
    Basic Features 
    About This Software 
     Platforms Supported 

Invece, il programma itera più volte sui nodi e produce quanto segue, stampando i nodi duplicati. (Guardando la lista dei nodi ad ogni iterazione, è ovvio il motivo per cui lo fa, ma non riesco a trovare un modo per ottenere la lista dei nodi che sto cercando.)

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

Qui è la sorgente XML File:

<?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> 

Ecco il programma 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 

ho potuto risolvere il problema, non la nidificazione elementi 'soggetto', cambiando i nomi degli argomenti di livello inferiore a qualcosa come 'SubTopic1' e ' SubTopic2' . Ma, voglio approfittare della strutturazione gerarchica XML integrata senza bisogno di nomi di elementi diversi; sembra che dovrei essere in grado di annidare elementi 'Argomento' e che ci dovrebbe essere un modo per sapere quale livello 'Argomento' sto guardando attualmente.

Ho provato un numero di diverse funzioni XPath senza molto successo.

+0

Se si desidera che l'uscita del primo si può semplicemente stampare il testo di ogni elemento - non mi è chiaro come lo structuting influisce sull'uscita voluta – Mark

risposta

8

getElementsByTagName è ricorsivo, riceverai tutti i discendenti con un tagname corrispondente. Poiché i tuoi argomenti contengono altri argomenti contenenti anche titoli, la chiamata otterrà i titoli più in basso più volte.

Se si consiglia di chiedere solo per tutti i corrispondenti figli diretti, e non si dispone di XPath disponibili, è possibile scrivere un semplice filtro, ad es .:

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

Grazie per il tentativo. Non ha funzionato, ma mi ha dato alcune idee. Le seguenti opere (la stessa idea generale; FWIW, il nodeType è ELEMENT_NODE): import xml.dom.minidom da xml.dom.minidom importazione Nodo dom = xml.dom.minidom.parse ("docmap.xml ") def getChildrenByTitle (nodo): per il bambino in node.childNodes: se child.localName == 'Titolo': rendimento bambino topic = dom.getElementsByTagName ('topic') per il nodo nella discussione: alist = getChildrenByTitle (nodo) per un alist: # Title = a.firstChild.data Title = a.childNodes [0] .nodeValue print Titolo – hWorks

+0

Oops si, intendevo ELEMENT non TEXT ovviamente! doh, corretto – bobince

7

Mettiamola quel commento qui ..

Grazie per il tentativo. Non ha funzionato, ma mi ha dato alcune idee. Le seguenti opere (la stessa idea generale; FWIW, il nodeType è 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

Chiamerei la funzione getTitle (o 'get_title'), e non restituire tutti gli elementi del titolo figlio immediato, ma solo il primo (poiché dovrebbe esserci un solo titolo per ogni bambino, comunque). –

+0

Forse questo è quello che non sto ottenendo. Voglio i titoli di tutti i bambini immediati. Forse un nome migliore sarebbe getTitlesOfChildren. – hWorks

3

È possibile utilizzare il seguente generatore a correre attraverso la lista e ottenere titoli con livelli di rientro:

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 

Se si prova con il file:

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

si otterrà una lista con i seguenti tuple:

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

È solo un'idea di base da mettere a punto, naturalmente. Se vuoi solo degli spazi all'inizio puoi codificarli direttamente nel generatore, anche se con il livello hai più flessibilità. È anche possibile rilevare automaticamente il primo livello (qui è solo un lavoro scarso di inizializzare il livello a -1 ...).

+0

Esattamente quello che ho cercato di fare tutto il giorno prima di imbattersi nei generatori. Grazie molto. – hWorks

1

funzione 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) 

tuo 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> 

l'output desiderato:

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

Penso che possono aiutare

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 

uscita:

My Document 
Overview 
Basic Features 
About This Software 
Platforms Supported