Python ElementTree modulul: Cum să ignore nume de fișiere XML pentru a localiza element de potrivire atunci când se utilizează metoda "a găsi", "findall"

Vreau să folosesc metoda de "findall" pentru a localiza unele elemente de sursă fișier xml în ElementTree module.

Cu toate acestea, sursa fișier xml (test.xml) are nume. Am trunchia o parte din fișier xml ca probă:

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

Mostră de cod python este mai jos:

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>

Deși se poate merge, pentru că nu există un spațiu de nume "{http://www.test.com}", a's foarte incomod pentru a adăuga un nume în fața fiecare etichetă.

Cum pot ignora nume atunci când se utilizează metoda de "găsi", "findall" și așa mai departe?

Comentarii la întrebare (2)

În loc de a modifica documentul XML în sine, l's cel mai bun pentru a analiza și apoi modifica tag-uri în consecință. În acest fel puteți gestiona mai multe spații de nume și nume pseudonime:

from io import StringIO  # for Python 2 import from StringIO instead
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    prefix, has_namespace, postfix = el.tag.partition('}')
    if has_namespace:
        el.tag = postfix  # strip all namespaces
root = it.root

Aceasta se bazează pe discuții aici: http://bugs.python.org/issue18304

Comentarii (5)

Dacă eliminați atributul xmlns din xml înainte de parsare atunci nu va't fi un nume precedat la fiecare etichetă în copac.

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)
Comentarii (3)

Răspunsurile atât de departe în mod explicit a pus nume valoare în script-ul. Pentru o mai generic soluție, mai degrabă aș extrage nume din xml:

import re
def get_namespace(element):
  m = re.match('\{.*\}', element.tag)
  return m.group(0) if m else ''

Și de a folosi în a găsi metoda:

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text
Comentarii (2)

Aici's o extensie a nonagon's-a răspuns, care, de asemenea, benzi de spații de nume pe atribute:

from StringIO import StringIO
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
    for at in el.attrib.keys(): # strip namespaces of attributes too
        if '}' in at:
            newat = at.split('}', 1)[1]
            el.attrib[newat] = el.attrib[at]
            del el.attrib[at]
root = it.root
Comentarii (0)

Îmbunătățirea răspunde de ericspod:

În loc de a schimba analiza modul global putem încheia într-un obiect sprijinirea cu construct.

from xml.parsers import expat

class DisableXmlNamespaces:
    def __enter__(self):
            self.oldcreate = expat.ParserCreate
            expat.ParserCreate = lambda encoding, sep: self.oldcreate(encoding, None)
    def __exit__(self, type, value, traceback):
            expat.ParserCreate = self.oldcreate

Acest lucru poate fi apoi utilizat după cum urmează

import xml.etree.ElementTree as ET
with DisableXmlNamespaces():
     tree = ET.parse("test.xml")

Frumusetea acestui mod este că ea nu se schimbă comportamentul de legătură, cod afara cu bloc. Am sfârșit prin a crea acest lucru după ce erori în alte biblioteci după utilizarea versiunea de ericspod care, de asemenea, s-a întâmplat să utilizați expat.

Comentarii (1)

Puteți utiliza elegant șir de formatare a construi la fel de bine:

ns='http://www.test.com'
el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))

sau, daca're sigur că PAID_OFF apare doar într-un singur nivel în arbore:

el2 = tree.findall(".//{%s}PAID_OFF" % ns)
Comentarii (0)

Daca're folosind ElementTree "și nu" cElementTree poți forța Expat să ignore nume de prelucrare prin înlocuireaParserCreate()`:

from xml.parsers import expat
oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

ElementTree încearcă să folosească Expat de asteptare ParserCreate() dar nu oferă opțiunea de a nu oferi un spațiu de nume separator șir, codul de mai sus va face ca acesta să fie ignorat, dar să fie avertizat, acest lucru ar putea rezolva alte lucruri.

Comentarii (4)

Las's combina nonagon's a răspunde cu mzjn's a răspunde la o întrebare legată de:

def parse_xml(xml_path: Path) -> Tuple[ET.Element, Dict[str, str]]:
    xml_iter = ET.iterparse(xml_path, events=["start-ns"])
    xml_namespaces = dict(prefix_namespace_pair for _, prefix_namespace_pair in xml_iter)
    return xml_iter.root, xml_namespaces

Folosind această funcție, avem:

  1. Creați un iterator pentru a obține ambele spații de nume și analizat un copac obiect.

  2. Repeta peste creat iterator pentru a obține spații de nume dict că putem mai târziu trece în fiecare găsim () " sau " findall()` sun ca dată de iMom0.

  3. Reveni analizat copac's element rădăcină obiect și spații de nume.

Cred că aceasta este cea mai bună abordare în jurul ca acolo's nici o manipulare, fie de o sursă XML sau care rezultă analizat xml.etree.ElementTree ieșire un fel de implicat.

Am'd place, de asemenea, de credit barny's a răspunde, cu furnizarea de o piesă esențială a puzzle-ului (pe care le puteți obține analizat rădăcină de iterator). Până ca de fapt am traversat XML tree de doua ori la cererea mea (o dată pentru a obține spații de nume, al doilea pentru o rădăcină).

Comentarii (0)

S-ar putea să fie târziu pentru asta, dar nu cred ca re.sub este o soluție bună.

Cu toate acestea rescrie xml.interpretoare.expat nu funcționează pentru Python 3.x versiuni,

Principalul vinovat este xml/etree/ElementTree.py se vedea partea de jos a codului sursă

# Import the C accelerators
try:
    # Element is going to be shadowed by the C implementation. We need to keep
    # the Python version of it accessible for some "creative" by external code
    # (see tests)
    _Element_Py = Element

    # Element, SubElement, ParseError, TreeBuilder, XMLParser
    from _elementtree import *
except ImportError:
    pass

Ceea ce e cam trist.

Soluția este de a scăpa de ea în primul rând.

import _elementtree
try:
    del _elementtree.XMLParser
except AttributeError:
    # in case deleted twice
    pass
else:
    from xml.parsers import expat  # NOQA: F811
    oldcreate = expat.ParserCreate
    expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

Testat pe Python 3.6.

Încercați "încerca" declarație este util în cazul în care undeva în codul de reincarcare sau importați un modul de două ori veți obține unele erori ciudate ca

  • maxim recursivitate adâncime depășit
  • AttributeError: XMLParser

btw blestemat pomul codul sursa arata foarte murdar.

Comentarii (0)