如何找到没有属性的xml节点

时间:2013-01-09 20:39:46

标签: python xml-parsing

我正在使用python 2.7并尝试解析下面的XML - 我要做的是创建一个包含语言属性的所有类型的python数组以及没有语言属性的数组。

我正在使用python模块import xml.etree.cElementTree as ET

我知道我可以通过语法找到语言属性在“fr”语言中的XML部分:


tree=ET.ElementTree(file='popups.xml')
root = tree.getroot()
for x in root.findall('alt[@{http://www.w3.org/XML/1998/namespace}lang="fr"]/alt'):
   print x.text

我真的不明白为什么我不能使用xml:lang而不是{http://www.w3.org/XML/1998/namespace}lang,但上面的内容似乎适用于Ubuntu 12.04

我想要找出的是“not”语法 - 其中XML部分没有任何语言属性

有人想过如何实现这个目标吗?

<genre>
  <alt>
        <alt genre="easy listening">lounge</alt>
        <alt genre="alternative">ska</alt>
  </alt>

  <alt xml:lang="fr">
        <alt genre="gospel">catholique</alt>
  </alt>
</genre>

2 个答案:

答案 0 :(得分:4)

您需要在xpath中使用完整的QName,因为stdlib ElementTree没有注册前缀的方法。我通常使用辅助函数来创建QNames:

def qname(prefix, element, map={'xml':'http://www.w3.org/XML/1998/namespace'}):
    return "{{{}}}{}".format(map[prefix], element)

标准库中的ElementTree实现不支持足够的XPath来轻松完成您想要的操作。但是,spec for xml:lang指定此属性的值由包含它的所有内容继承,类似于xml:basexmlns命名空间声明。因此,作为替代方案,我们可以在所有元素上明确语言设置:

xml_lang = qname('xml', 'lang')

def set_xml_lang(root, defaultlang=''):
    xml_lang = qname('xml', 'lang')
    for item in root:
        try:
            lang = item.attrib[xml_lang]
        except KeyError, err:
            item.set(xml_lang, defaultlang)
            lang = defaultlang
        set_xml_lang(item, lang)

set_xml_lang(root)

namespaces = {'xml':'http://www.w3.org/XML/1998/namespace'}
# Every element in root now has an xml:lang attribute
# so XPath is easy now:
alts_with_no_lang = root.findall('alt[@{{{xml}}}lang=""]'.format(**namespaces))

如果您愿意使用lxml,那么您对“lang”的使用会更加强大,因为它遵循完整的XPath 1.0规范。特别是,您可以使用lang()函数:

import lxml.etree as ET

root = ET.fromstring(xml)

print root.xpath('//alt[lang("fr")]')

作为奖励,它将具有正确的lang()语义,例如不区分大小写并且对语言区域很聪明(例如lang('en')也适用于xml:lang="en-US"

很遗憾,您无法使用lang()来确定节点的语言。您需要找到第一个xml:lang祖先并使用它:

mylang = node.xpath('(ancestor-or-self::*/@xml:lang)[1]')

全部放在一起,以匹配没有语言的节点:

tree.xpath('//alt[not((ancestor-or-self::*/@xml:lang)[1])]')

答案 1 :(得分:1)

  

我真的不明白为什么我不能使用xml:lang而不是{http://www.w3.org/XML/1998/namespace}lang,但上面的内容似乎适用于Ubuntu 12.04

使用xpath方法(cElementTree not )可以更轻松地尝试做什么,其中包括从import lxml.etree as et root = et.parse(open('mydoc.xml')).getroot() for x in root.xpath('alt[not(@xml:lang)]/alt'): print x.text 中读取命名空间标签文档的根元素,所以你可以这样问:

not(@attr)

我以前不熟悉的{{1}}语法,但Google搜索“xpath find element without attribute”非常有用。