我正在尝试使用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
在编写此解析器时,这将节省大量的击键次数。这可能吗?我在文档中遗漏了什么吗?
答案 0 :(得分:22)
也许使用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
答案 1 :(得分:5)
我找不到从元素中获取非命名空间标记名称的方法 - 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方法搜索带有“any namespace”的标签(例如{*}test
)。
已更新:请注意,lxml不会构建仅包含{ or }
的标记 - 它会引发ValueError:无效的标记名称,因此可以安全地假设其元素为标记名称以{
开头是平衡的。
lxml.etree.Element('{foo')
ValueError: Invalid tag name
答案 2 :(得分:4)
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":
答案 3 :(得分:2)
为了节省在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]
答案 4 :(得分: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
答案 5 :(得分:0)
这是我恢复真实(源)xml标记名称的解决方案
假设我们有xml_node
变量,则是lxml Element
的实例
之前:{http://some/namespace/url}TagName
(从xml_node.tag
道具中读取)
之后: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