Python的ElementTree的模块:如何使用该方法时,忽略XML文件的命名空间来定位匹配的元

2019-06-21 05:01发布

我想用“的findAll”的方法来定位的ElementTree模块中的源XML文件中的一些元素。

然而,源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>

样品Python代码低于:

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”,这是非常不方便,在每个标签的前面加上一个命名空间。

我怎样才能使用的“发现”,“的findall”等方法时忽略的命名空间?

Answer 1:

而不是修改XML文档本身,这是最好的结果,解析它,然后修改标签。 这样,您就可以处理多个命名空间和命名空间别名:

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
root = it.root

这是基于这里的讨论: http://bugs.python.org/issue18304



Answer 2:

如果您解析它之前删除的xmlns从XML属性则不会有预置到树中的每个标签的命名空间。

import re

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


Answer 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


Answer 4:

下面是一个扩展九边形的回答,这也剔除掉命名空间属性:

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


Answer 5:

提高对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这也正好利用外籍人士后得到的不相关的库错误后创建此。



Answer 6:

您可以使用优雅的字符串格式化结构以及:

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

或者,如果你确信PAID_OFF只出现在树一个级别:

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


Answer 7:

如果您使用ElementTree ,而不是cElementTree你可以强制外籍人士通过更换忽略命名空间处理ParserCreate()

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

ElementTree尝试通过调用使用外籍人士ParserCreate()但没有提供选择不提供命名空间分隔字符串,上面的代码将导致其成为忽略,但被警告,这可能打破其他东西。



Answer 8:

我可能是这晚,但我不认为re.sub是一个很好的解决方案。

然而重写xml.parsers.expat不为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。

尝试try声明的情况下,是非常有用的地方在你的代码重新加载或导入模块两次,你得到一些奇怪的错误,如

  • 最大递归深度超过
  • AttributeError的:XMLParser的

顺便说一句该死的etree源代码看起来很凌乱。



文章来源: Python ElementTree module: How to ignore the namespace of XML files to locate matching element when using the method “find”, “findall”