Eu quero usar o método "findall" para localizar alguns elementos do arquivo xml de origem no módulo ElementTree.
No entanto, o arquivo xml de origem (test.xml) possui um espaço para nome. Eu truncar parte do arquivo xml como exemplo:
<?xml version="1.0" encoding="iso-8859-1"?>
<XML_HEADER xmlns="http://www.test.com">
<TYPE>Updates</TYPE>
<DATE>9/26/2012 10:30:34 AM</DATE>
<COPYRIGHT_NOTICE>All Rights Reserved.</COPYRIGHT_NOTICE>
<LICENSE>newlicense.htm</LICENSE>
<DEAL_LEVEL>
<PAID_OFF>N</PAID_OFF>
</DEAL_LEVEL>
</XML_HEADER>
O exemplo de código python está abaixo:
from xml.etree import ElementTree as ET
tree = ET.parse(r"test.xml")
el1 = tree.findall("DEAL_LEVEL/PAID_OFF") # Return None
el2 = tree.findall("{http://www.test.com}DEAL_LEVEL/{http://www.test.com}PAID_OFF") # Return <Element '{http://www.test.com}DEAL_LEVEL/PAID_OFF' at 0xb78b90>
Embora possa funcionar, porque existe um espaço para nome "{http://www.test.com}", é muito inconveniente adicionar um espaço para nome na frente de cada tag.
Como posso ignorar o espaço para nome ao usar o método "find", "findall" e assim por diante?
python
namespaces
find
elementtree
findall
KevinLeng
fonte
fonte
tree.findall("xmlns:DEAL_LEVEL/xmlns:PAID_OFF", namespaces={'xmlns': 'http://www.test.com'})
conveniente o suficiente?tree.findall("{0}DEAL_LEVEL/{0}PAID_OFF".format('{http://www.test.com}'))
Respostas:
Em vez de modificar o próprio documento XML, é melhor analisá-lo e modificar as tags no resultado. Dessa forma, você pode lidar com vários espaços para nome e aliases de espaço para nome:
Isso se baseia na discussão aqui: http://bugs.python.org/issue18304
Atualização: em
rpartition
vez departition
garantir que você obtenha o nome da tag,postfix
mesmo que não haja espaço para nome. Assim, você pode condensar:fonte
et.findall('{*}sometag')
. E também está manipulando a própria árvore de elementos, não apenas "realize a pesquisa ignorando os espaços para nome neste momento, sem analisar novamente o documento etc., mantendo as informações do espaço para nome". Bem, nesse caso, você notavelmente precisa percorrer a árvore e ver por si mesmo se o nó corresponde aos seus desejos após remover o espaço para nome.Se você remover o atributo xmlns do xml antes de analisá-lo, não haverá um espaço para nome anexado a cada marca na árvore.
fonte
=
sinal de igual.As respostas até agora colocam explicitamente o valor do espaço para nome no script. Para uma solução mais genérica, prefiro extrair o namespace do xml:
E use-o no método find:
fonte
namespace
Aqui está uma extensão da resposta de nonagon, que também remove os namespaces dos atributos:
UPDATE: adicionado
list()
para que o iterador funcione (necessário para o Python 3)fonte
Melhorando a resposta de ericspod:
Em vez de alterar o modo de análise globalmente, podemos agrupar isso em um objeto que suporta a construção with.
Isso pode ser usado da seguinte maneira
A vantagem dessa maneira é que ela não altera nenhum comportamento para código não relacionado fora do bloco with. Acabei criando isso depois de obter erros em bibliotecas não relacionadas depois de usar a versão ericspod, que também utilizava expat.
fonte
xml.etree.ElementTree.XMLParser
está de alguma forma otimizado e a correção de macacosexpat
não tem absolutamente nenhum efeito.Você também pode usar a construção elegante de formatação de string:
ou, se tiver certeza de que PAID_OFF aparece apenas em um nível na árvore:
fonte
Se você estiver usando
ElementTree
e não,cElementTree
poderá forçar o Expat a ignorar o processamento do espaço para nome substituindoParserCreate()
:ElementTree
tenta usar o Expat chamando,ParserCreate()
mas não fornece opção para não fornecer uma cadeia separadora de namespace; o código acima fará com que seja ignorado, mas avise que isso pode quebrar outras coisas.fonte
ElementTree.fromstring(s, parser=None)
estou tentando passar um analisador para ele.Eu posso estar atrasado para isso, mas eu não acho
re.sub
seja uma boa solução.No entanto, a reescrita
xml.parsers.expat
não funciona nas versões do Python 3.x,O principal culpado é a
xml/etree/ElementTree.py
parte inferior do código fonteO que é meio triste.
A solução é se livrar dele primeiro.
Testado em Python 3.6.
Experimentar
try
instrução é útil caso, em algum lugar do seu código, você recarregue ou importe um módulo duas vezes e receba erros estranhos, comobtw caramba o código fonte etree parece realmente confuso.
fonte
Vamos combinar a resposta de nonagon com a resposta de mzjn para uma pergunta relacionada :
Usando esta função, nós:
Crie um iterador para obter os espaços para nome e um objeto de árvore analisada .
Faça uma iteração sobre o iterador criado para obter os ditames dos namespaces que podemos passar posteriormente em cada um
find()
oufindall()
chamar conforme sugerido pelo iMom0 .Eu acho que essa é a melhor abordagem geral, já que não há manipulação de um XML de origem ou resultado analisado
xml.etree.ElementTree
saída .Eu também gostaria de creditar a resposta de Barny por fornecer uma peça essencial desse quebra-cabeça (que você pode obter a raiz analisada do iterador). Até que eu realmente percorri a árvore XML duas vezes no meu aplicativo (uma vez para obter namespaces, o segundo para uma raiz).
fonte
find()
efindall()
. Você apenas alimenta esses métodos com o ditado do namespaceparse_xml()
e usa o prefixo do namespace em suas consultas. Por exemplo:et_element.findall(".//some_ns_prefix:some_xml_tag", namespaces=xml_namespaces)