Obtenha o valor do elemento com minidom com Python

109

Estou criando um front-end GUI para a API Eve Online em Python.

Eu obtive com sucesso os dados XML de seu servidor.

Estou tentando obter o valor de um nó chamado "nome":

from xml.dom.minidom import parse
dom = parse("C:\\eve.xml")
name = dom.getElementsByTagName('name')
print name

Isso parece encontrar o nó, mas a saída está abaixo:

[<DOM Element: name at 0x11e6d28>]

Como posso fazer com que ele imprima o valor do nó?

RailsSon
fonte
5
Está começando a parecer que a resposta para a maioria das perguntas "minidom" é "usar ElementTree".
Warren P

Respostas:

156

Deveria ser apenas

name[0].firstChild.nodeValue
eduffy
fonte
4
Quando eu nomeio [0] .nodeValue é retornado "Nenhum", só para testar eu passei o nome [0] .nodeName e ele me deu "nome" que está correto. Alguma ideia?
RailsSon
28
E o nome [0] .firstChild.nodeValue?
eduffy
7
Esteja ciente de que você não está contando com detalhes de implementação no gerador de xml. Não há garantias de que o primeiro filho seja o nó de texto nem o único nó de texto em qualquer caso em que possa haver mais de um nó filho.
Henrik Gustafsson
53
Por que alguém projetaria uma biblioteca em que o nodeValue de <name> Smith </name> fosse qualquer coisa menos "Smith" ?! Essa pequena pepita me custou 30 minutos arrancando meus cabelos. Estou careca agora. Obrigado, minidom.
Assaf Lavie
10
É apenas por causa da maneira como eles o projetaram para trabalhar com html, para permitir elementos como este <nodeA> Some Text <nodeinthemiddle> __complex__structure__ </nodeinthemiddle> Mais texto </nodeA>, neste caso você acha que nodeA's nodeValue deve conter todo o texto, incluindo a estrutura complexa, ou simplesmente 2 nós de texto e o nó do meio. Não é a maneira mais agradável de ver isso, mas posso ver por que eles fizeram isso.
Josh Mc
60

Provavelmente algo assim se for a parte do texto que você deseja ...

from xml.dom.minidom import parse
dom = parse("C:\\eve.xml")
name = dom.getElementsByTagName('name')

print " ".join(t.nodeValue for t in name[0].childNodes if t.nodeType == t.TEXT_NODE)

A parte do texto de um nó é considerada um nó em si mesma, colocada como nó filho daquele que você solicitou. Portanto, você desejará percorrer todos os seus filhos e encontrar todos os nós filhos que são nós de texto. Um nó pode ter vários nós de texto; por exemplo.

<name>
  blabla
  <somestuff>asdf</somestuff>
  znylpx
</name>

Você quer 'blabla' e 'znylpx'; daí o "" .join (). Você pode querer substituir o espaço por uma nova linha ou algo assim, ou talvez por nada.

Henrik Gustafsson
fonte
12

você pode usar algo assim. Funcionou para mim

doc = parse('C:\\eve.xml')
my_node_list = doc.getElementsByTagName("name")
my_n_node = my_node_list[0]
my_child = my_n_node.firstChild
my_text = my_child.data 
print my_text
samaksh
fonte
8

Sei que essa pergunta é bem antiga agora, mas achei que seria mais fácil para você com ElementTree

from xml.etree import ElementTree as ET
import datetime

f = ET.XML(data)

for element in f:
    if element.tag == "currentTime":
        # Handle time data was pulled
        currentTime = datetime.datetime.strptime(element.text, "%Y-%m-%d %H:%M:%S")
    if element.tag == "cachedUntil":
        # Handle time until next allowed update
        cachedUntil = datetime.datetime.strptime(element.text, "%Y-%m-%d %H:%M:%S")
    if element.tag == "result":
        # Process list of skills
        pass

Eu sei que não é muito específico, mas acabei de descobrir, e até agora é muito mais fácil entender do que o minidom (já que tantos nós são essencialmente espaços em branco).

Por exemplo, você tem o nome da tag e o texto real juntos, exatamente como você provavelmente esperaria:

>>> element[0]
<Element currentTime at 40984d0>
>>> element[0].tag
'currentTime'
>>> element[0].text
'2010-04-12 02:45:45'e
LarrikJ
fonte
8

A resposta acima está correta, a saber:

name[0].firstChild.nodeValue

No entanto, para mim, como para outros, meu valor estava mais abaixo na árvore:

name[0].firstChild.firstChild.nodeValue

Para encontrar isso, usei o seguinte:

def scandown( elements, indent ):
    for el in elements:
        print("   " * indent + "nodeName: " + str(el.nodeName) )
        print("   " * indent + "nodeValue: " + str(el.nodeValue) )
        print("   " * indent + "childNodes: " + str(el.childNodes) )
        scandown(el.childNodes, indent + 1)

scandown( doc.getElementsByTagName('text'), 0 )

Executar isso para meu arquivo SVG simples criado com o Inkscape me deu:

nodeName: text
nodeValue: None
childNodes: [<DOM Element: tspan at 0x10392c6d0>]
   nodeName: tspan
   nodeValue: None
   childNodes: [<DOM Text node "'MY STRING'">]
      nodeName: #text
      nodeValue: MY STRING
      childNodes: ()
nodeName: text
nodeValue: None
childNodes: [<DOM Element: tspan at 0x10392c800>]
   nodeName: tspan
   nodeValue: None
   childNodes: [<DOM Text node "'MY WORDS'">]
      nodeName: #text
      nodeValue: MY WORDS
      childNodes: ()

Usei xml.dom.minidom, os vários campos são explicados nesta página, MiniDom Python.

LazyBrush
fonte
2

Eu tive um caso semelhante, o que funcionou para mim foi:

name.firstChild.childNodes [0] .data

XML deveria ser simples e realmente é, e não sei por que o minidom do python tornou isso tão complicado ... mas é assim que é feito

robertzp
fonte
2

Aqui está uma resposta ligeiramente modificada de Henrik para vários nós (ou seja, quando getElementsByTagName retorna mais de uma instância)

images = xml.getElementsByTagName("imageUrl")
for i in images:
    print " ".join(t.nodeValue for t in i.childNodes if t.nodeType == t.TEXT_NODE)
khany
fonte
2

A pergunta foi respondida, minha contribuição consiste em esclarecer uma coisa que pode confundir os iniciantes:

Algumas das respostas sugeridas e corretas usadas firstChild.datae outras usadas em seu firstChild.nodeValuelugar. Caso você esteja se perguntando qual é a diferença entre eles, lembre-se de que fazem a mesma coisa porque nodeValueé apenas um apelido para data.

A referência à minha declaração pode ser encontrada como um comentário sobre o código-fonte do minidom :

# nodeValueé um alias paradata

Billal Begueradj
fonte
0

É uma árvore e pode haver elementos aninhados. Experimentar:

def innerText(self, sep=''):
    t = ""
    for curNode in self.childNodes:
        if (curNode.nodeType == Node.TEXT_NODE):
            t += sep + curNode.nodeValue
        elif (curNode.nodeType == Node.ELEMENT_NODE):
            t += sep + curNode.innerText(sep=sep)
    return t
TextGeek
fonte