繁体   English   中英

Lxml 元素与命名空间相等

[英]Lxml element equality with namespaces

我正在尝试使用 Lxml 来解析 .docx 文档的内容。 我知道 lxml 用实际的命名空间替换了命名空间前缀,但是这使得检查我正在使用的元素标签类型变得非常痛苦。 我希望能够做类似的事情

if (someElement.tag == "w:p"):

但是由于 lxml 坚持在 te ful 命名空间之前添加我要么必须做类似的事情

if (someElemenet.tag == "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p'):

或者像这样从元素的 nsmap 属性中查找完整的命名空间名称

targetTag = "{%s}p" % someElement.nsmap['w']
if (someElement.tag == targetTag):

如果有更简单的方法来说服 lxml

  1. 给我没有附加命名空间的标签字符串,我可以使用前缀属性和这些信息来检查我正在使用哪个标签或
  2. 只需给我使用前缀的标签字符串

在编写这个解析器时,这将节省大量的击键次数。 这可能吗? 我在文档中遗漏了什么吗?

也许使用local-name()

import lxml.etree as ET
tree = ET.fromstring('<root xmlns:f="foo"><f:test/></root>')
elt=tree[0]
print(elt.xpath('local-name()'))
# test

etree.Qname应该能够得到你想要的。

from lxml import etree

# [...]

tag = etree.QName(someElement)

print(tag.namespace, tag.localname)

对于您的示例标签,这将输出:

http://schemas.openxmlformats.org/wordprocessingml/2006/main p

请注意, QName将采用Element对象或字符串(例如来自Element.tag )。

而且,正如您所注意到的,您还可以使用Element.nsmap从任意前缀映射到命名空间。

所以像这样:

if tag.namespace == someElement.nsmap["w"] and tag.localname == "p":

我找不到从元素中获取非命名空间标记名称的方法——lxml 考虑标记名称的完整命名空间部分。 这里有一些可能会有所帮助的选项。

您还可以使用QName类来构造一个命名空间标记以进行比较:

import lxml.etree
from lxml.etree import QName

tree = lxml.etree.fromstring('<root xmlns:f="foo"><f:test/></root>')
qn = QName(tree.nsmap['f'], 'test')
assert tree[0].tag == qn

如果您需要裸标签名称,则必须编写一个实用程序函数来提取它:

def get_bare_tag(elem):
    return elem.tag.rsplit('}', 1)[-1]

assert get_bare_tag(tree[0]) == 'test'

不幸的是,据我所知,您无法使用 lxml 的 xpath / find 方法搜索带有“任何名称空间”(例如{*}test )的标签。

更新:请注意,lxml 不会构造仅包含{ or }的标签——它会引发 ValueError: invalid tag name,因此可以安全地假设标签名称以{开头的元素是平衡的。

lxml.etree.Element('{foo')
ValueError: Invalid tag name

为了节省在 docx 中查找像p (我认为是段落)或 xlsx 中的c (单元格)这样的大容量标签时的时间,通常在全局或类级别设置一次完整标签:

WPML_URI = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
tag_p = WPML_URI + 'p'
tag_t = WPML_URI + 't'

我从未见过解释为什么要使用QName()

另一方面,给定一个完整的标签,您可以轻松提取基本标签:

base_tag = full_tag.rsplit("}", 1)[-1]

我不是 Python 专家,但我也遇到了这个问题(Windows 7“联系人”文件)。 我为 lxml 系统编写了以下函数。

此函数接受一个元素,并返回其标记,其前缀替换为文件的 ns 标记。

from lxml import etree

def denstag(ee):
  tag = ee.tag
  for ns in ee.nsmap:
    prefix = "{"+ee.nsmap[ns]+"}"
    if tag.startswith(prefix):               
      return ns+":"+tag[len(prefix):]
  return tag

这是我恢复真实(源)xml 标记名称的解决方案

假设我们有xml_node变量,一个 lxml Element的实例

之前: {http://some/namespace/url}TagName (从xml_node.tag prop 中读取)

之后: nsprefix:TagName (作为xml_get_real_tag_name(xml_node)

def xml_get_real_tag_name(xml_node):
    """Replace lxml '{http://some/namespace/url}TagName' with regular 'nsprefix:TagName' string
    Args:
        xml_node (lxml.etree.Element) Source xml node entity
    Returns:
        str
    """
    if '{' in xml_node.tag:
    return ':'.join([xml_node.prefix, etree.QName(xml_node).localname])
else:
    return xml_node.tag

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM