Модуль Python ElementTree: как игнорировать пространства имен XML-файлов, чтобы найти соответствующий элемент при использовании метода на "обнаружить", "в метод findAll и"

Я хочу использовать метод "в метод findAll", чтобы найти некоторые элементы из исходного XML-файла в модуле ElementTree.

Однако XML-файл источник (test.xml) имеет пространства имен. Я усечь часть XML-файл в качестве образца:

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

Образец кода ниже:

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>

Хотя это может работать, потому что есть пространство имен " и{http://www.test.com}", Он's очень неудобно, чтобы добавить пространства имен перед каждым тегом.

Как я могу игнорировать пространства имен при использовании метода на "обнаружить", "в метод findAll" и так далее?

Комментарии к вопросу (2)

Вместо изменения самого XML-документа, он'ы лучше разобрать его и измените теги в результате. Таким образом, вы можете обрабатывать несколько имен и псевдонимов пространств имен:

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

Это основано на обсуждении здесь: http://bugs.python.org/issue18304

Комментарии (5)

Если убрать xmlns атрибут из XML до разбора, то есть выиграл'т быть пространство имен префикс для каждого тега в дереве.

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)
Комментарии (3)

Ответы до сих пор явно поставил значение пространства имен в сценарии. Для более общее решение, я бы скорее извлечь пространства имен из XML:

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

И использовать его в метод find:

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text
Комментарии (2)

Здесь'ы расширение девятиугольника'ы ответ, который также полоски от имен атрибутов:

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
Комментарии (0)

Улучшая ответ на ericspod:

Вместо того, чтобы изменить режим парсинга в мире мы можем обернуть это в объект обслуживания с конструкции.

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

Это может быть использовано следующим образом

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

Красота этого способа заключается в том, что он не вносит никаких изменений в поведении для несвязанных код вне блока. Я в конечном итоге создание этого после получения ошибки в несвязанных библиотеки после использования версии ericspod которой также довелось пользоваться эмигранта.

Комментарии (1)

Вы можете использовать элегантный форматирование строки строительства, а также:

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

или, если вы're уверен, что PAID_OFF появляется только в одном уровне в дереве:

el2 = tree.findall(".//{%s}PAID_OFF" % ns)
Комментарии (0)

Если вы'вновь с помощью ElementTree, а неглавноевы можете заставить эмигранта игнорировать обработку пространства имен, заменивParserCreate()`:

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

ElementTree пытается использовать эмигрант по призванию ParserCreate (), но не дает возможность не предоставляет разделитель пространства имен строки, приведенный выше код вызовет его игнорировать, но быть предупрежденным, это может сломать другие вещи.

Комментарии (4)

Позвольте'ы комбината девятиугольника'ы ответ с mzjn'ы ответ на соответствующий вопрос:

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

С помощью этой функции мы:

  1. Создать итератор, чтобы получить оба пространства имен и разобранный объект Дерево.

  2. Перебрать созданный итератор, чтобы получить пространство имен дикт, что мы можем впоследствии, проходят в каждом найти () " или " метод findAll звоните ()` как предложено по iMom0.

  3. Возвращение дерева'ы корневого элемента объекта и пространства имен.

Я думаю, что это лучший подход, все вокруг, как там's нет манипуляций или из источника XML или в результате проанализированного в формате XML.etree.Выход ElementTree не участвовала.

Я'd, как и кредит Барни'ы ответ с предоставлением важной частью этой головоломки (которые вы можете получить разобранный корень из итератора). Пока что я на самом деле проходится дважды XML-дерево в мое приложение (один раз, чтобы получить пространство имен, второй на корень).

Комментарии (0)

Я может быть поздно, но я не думаю, что `повторно.суб-хорошее решение.

Однако переписать в XML.парсеров.экспат` не работает для Python 3.X версий,

Главным виновником является xml/etree/ElementTree.py` смотрите внизу исходный код

# 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

И это довольно печально.

Решение избавиться от него первым.

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)

Проверена на Python 3.6.

Попробовать "попробовать" инструкция полезна в случае, если где-то в коде вы перезагрузить или импортировать модуль в два раза, вы получаете некоторые странные ошибки, такие как

  • максимальная глубина рекурсии превысил
  • AttributeError: XML-Парсер

кстати блин исходный код etree выглядит очень неаккуратно.

Комментарии (0)