在 lxml 中查找具有未知命名空间的元素
Find element that has unknown namespace in lxml
我有一个 XML 有很多级别。每个级别都可以附加名称空间。我想 find
一个我知道其名称但不知道其名称空间的特定元素。例如:
my_file.xml
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="aaa:bbb:ccc:ddd:eee">
<country name="Liechtenstein" xmlns="aaa:bbb:ccc:liechtenstein:eee">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore" xmlns="aaa:bbb:ccc:singapore:eee">
<continent>Asia</continent>
<holidays>
<christmas>Yes</christmas>
</holidays>
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama" xmlns="aaa:bbb:ccc:panama:eee">
<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
import lxml.etree as etree
tree = etree.parse('my_file.xml')
root = tree.getroot()
cntry_node = root.find('.//country')
上面的 find
对 cntry_node
没有 return 任何东西。在我的真实数据中,层次比这个例子更深。 lxml 文档讨论了名称空间。当我这样做时:
root.nsmap
我看到了这个:
{None: 'aaa:bbb:ccc:ddd:eee'}
如果有人可以解释如何访问完整的 nsmap
and/or 如何将其用于 find
特定元素?非常感谢。
您可以声明所有名称空间,但考虑到示例的结构 xml,我认为您最好完全忽略名称空间并只使用 local-name()
;所以
cntry_node = root.xpath('.//*[local-name()="country"]')
cntry_node
returns
[<Element {aaa:bbb:ccc:liechtenstein:eee}country at 0x1cddf1d4680>,
<Element {aaa:bbb:ccc:singapore:eee}country at 0x1cddf1d47c0>,
<Element {aaa:bbb:ccc:panama:eee}country at 0x1cddf1d45c0>]
另一种选择是使用 {*}
作为命名空间通配符...
cntry_node = root.find('.//{*}country')
注意:这仅适用于 find()
、findall()
、iter()
等;不是 xpath()
.
See here了解更多详情。
nsmap
不是 XML 文档的所有名称空间的全局集合
我相信您的印象是 nsmap
是 所有 名称空间的集合,这些名称空间出现在 XML 文档中。并且该集合在解析文档后可用。事实并非如此。
nsmap
只允许您访问一个元素的命名空间定义。所以这个:
root = tree.getroot()
root.nsmap
为您提供在 root
元素的上下文中已知的命名空间定义。请记住,“root”只是一个 Python 变量的名称,实际上包含 XML 文档的最外层元素(我知道这一点是因为你调用了 getroot()
)。您文档的最外层元素是:
<data xmlns="aaa:bbb:ccc:ddd:eee">
所以它的 nsmap 应该包含
{None: 'aaa:bbb:ccc:ddd:eee'}
(nsmap 中有 None
,因为这是一个没有命名空间前缀的默认命名空间,可以到达 None
所在的位置。)
XML 文档结构很糟糕
通常,处理命名空间的最佳方法是自己定义它们(而不是从输入文档中获取它们)。假设我们要查找以下元素:
<country name="Liechtenstein" xmlns="aaa:bbb:ccc:liechtenstein:eee">
此 country
元素位于 默认名称空间 中,名称空间 URI 为“aaa:bbb:ccc:liechtenstein:eee”。要用lxml查找,定义一个映射:
my_own_namespace_mapping = {'prefix': 'aaa:bbb:ccc:liechtenstein:eee'}
然后在检索节点时使用它:
root.xpath('.//prefix:country', namespaces=my_own_namespace_mapping)
[<Element {aaa:bbb:ccc:liechtenstein:eee}country at 0x7fea87f363f8>]
但是,对于您的输入文档,您似乎需要为每个 country
元素单独执行此操作,因为它们各自位于自己的默认命名空间中:
root.xpath('.//prefix:country', namespaces={'prefix': 'aaa:bbb:ccc:singapore:eee'})
[<Element {aaa:bbb:ccc:singapore:eee}country at 0x7fea879cfd40>]
等等。这是非常不切实际的,不是因为 lxml 或命名空间很复杂,而是因为有人设计这种 XML 格式很糟糕。
顺便说一句,一旦找到其中一个元素,您可以再次使用 nsmap
来测试我上面所说的是否正确:
root.xpath('.//prefix:country', namespaces={'prefix': 'aaa:bbb:ccc:liechtenstein:eee'})[0].nsmap
{None: 'aaa:bbb:ccc:liechtenstein:eee'}
我有一个 XML 有很多级别。每个级别都可以附加名称空间。我想 find
一个我知道其名称但不知道其名称空间的特定元素。例如:
my_file.xml
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="aaa:bbb:ccc:ddd:eee">
<country name="Liechtenstein" xmlns="aaa:bbb:ccc:liechtenstein:eee">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore" xmlns="aaa:bbb:ccc:singapore:eee">
<continent>Asia</continent>
<holidays>
<christmas>Yes</christmas>
</holidays>
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama" xmlns="aaa:bbb:ccc:panama:eee">
<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
import lxml.etree as etree
tree = etree.parse('my_file.xml')
root = tree.getroot()
cntry_node = root.find('.//country')
上面的 find
对 cntry_node
没有 return 任何东西。在我的真实数据中,层次比这个例子更深。 lxml 文档讨论了名称空间。当我这样做时:
root.nsmap
我看到了这个:
{None: 'aaa:bbb:ccc:ddd:eee'}
如果有人可以解释如何访问完整的 nsmap
and/or 如何将其用于 find
特定元素?非常感谢。
您可以声明所有名称空间,但考虑到示例的结构 xml,我认为您最好完全忽略名称空间并只使用 local-name()
;所以
cntry_node = root.xpath('.//*[local-name()="country"]')
cntry_node
returns
[<Element {aaa:bbb:ccc:liechtenstein:eee}country at 0x1cddf1d4680>,
<Element {aaa:bbb:ccc:singapore:eee}country at 0x1cddf1d47c0>,
<Element {aaa:bbb:ccc:panama:eee}country at 0x1cddf1d45c0>]
另一种选择是使用 {*}
作为命名空间通配符...
cntry_node = root.find('.//{*}country')
注意:这仅适用于 find()
、findall()
、iter()
等;不是 xpath()
.
See here了解更多详情。
nsmap
不是 XML 文档的所有名称空间的全局集合
我相信您的印象是 nsmap
是 所有 名称空间的集合,这些名称空间出现在 XML 文档中。并且该集合在解析文档后可用。事实并非如此。
nsmap
只允许您访问一个元素的命名空间定义。所以这个:
root = tree.getroot()
root.nsmap
为您提供在 root
元素的上下文中已知的命名空间定义。请记住,“root”只是一个 Python 变量的名称,实际上包含 XML 文档的最外层元素(我知道这一点是因为你调用了 getroot()
)。您文档的最外层元素是:
<data xmlns="aaa:bbb:ccc:ddd:eee">
所以它的 nsmap 应该包含
{None: 'aaa:bbb:ccc:ddd:eee'}
(nsmap 中有 None
,因为这是一个没有命名空间前缀的默认命名空间,可以到达 None
所在的位置。)
XML 文档结构很糟糕
通常,处理命名空间的最佳方法是自己定义它们(而不是从输入文档中获取它们)。假设我们要查找以下元素:
<country name="Liechtenstein" xmlns="aaa:bbb:ccc:liechtenstein:eee">
此 country
元素位于 默认名称空间 中,名称空间 URI 为“aaa:bbb:ccc:liechtenstein:eee”。要用lxml查找,定义一个映射:
my_own_namespace_mapping = {'prefix': 'aaa:bbb:ccc:liechtenstein:eee'}
然后在检索节点时使用它:
root.xpath('.//prefix:country', namespaces=my_own_namespace_mapping)
[<Element {aaa:bbb:ccc:liechtenstein:eee}country at 0x7fea87f363f8>]
但是,对于您的输入文档,您似乎需要为每个 country
元素单独执行此操作,因为它们各自位于自己的默认命名空间中:
root.xpath('.//prefix:country', namespaces={'prefix': 'aaa:bbb:ccc:singapore:eee'})
[<Element {aaa:bbb:ccc:singapore:eee}country at 0x7fea879cfd40>]
等等。这是非常不切实际的,不是因为 lxml 或命名空间很复杂,而是因为有人设计这种 XML 格式很糟糕。
顺便说一句,一旦找到其中一个元素,您可以再次使用 nsmap
来测试我上面所说的是否正确:
root.xpath('.//prefix:country', namespaces={'prefix': 'aaa:bbb:ccc:liechtenstein:eee'})[0].nsmap
{None: 'aaa:bbb:ccc:liechtenstein:eee'}