saf*_*fsd 26 python xml lxml elementtree xml-namespaces
我正在尝试解析OpenOffice ODS电子表格中的内容.ods格式本质上只是一个包含许多文档的zipfile.电子表格的内容存储在'content.xml'中.
import zipfile
from lxml import etree
zf = zipfile.ZipFile('spreadsheet.ods')
root = etree.parse(zf.open('content.xml'))
Run Code Online (Sandbox Code Playgroud)
电子表格的内容位于单元格中:
table = root.find('.//{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table')
Run Code Online (Sandbox Code Playgroud)
我们也可以直接寻找行:
rows = root.findall('.//{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table-row')
Run Code Online (Sandbox Code Playgroud)
各个元素知道命名空间:
>>> table.nsmap['table']
'urn:oasis:names:tc:opendocument:xmlns:table:1.0'
Run Code Online (Sandbox Code Playgroud)
如何在find/findall中直接使用命名空间?
显而易见的解决方案不起作用.
试图从表中获取行:
>>> root.findall('.//table:table')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "lxml.etree.pyx", line 1792, in lxml.etree._ElementTree.findall (src/lxml/lxml.etree.c:41770)
File "lxml.etree.pyx", line 1297, in lxml.etree._Element.findall (src/lxml/lxml.etree.c:37027)
File "/usr/lib/python2.6/dist-packages/lxml/_elementpath.py", line 225, in findall
return list(iterfind(elem, path))
File "/usr/lib/python2.6/dist-packages/lxml/_elementpath.py", line 200, in iterfind
selector = _build_path_iterator(path)
File "/usr/lib/python2.6/dist-packages/lxml/_elementpath.py", line 184, in _build_path_iterator
selector.append(ops[token[0]](_next, token))
KeyError: ':'
Run Code Online (Sandbox Code Playgroud)
jfs*_*jfs 21
如果root.nsmap
包含table
名称空间前缀,那么您可以:
root.xpath('.//table:table', namespaces=root.nsmap)
Run Code Online (Sandbox Code Playgroud)
findall(path)
接受{namespace}name
语法而不是namespace:name
.因此path
,在将{namespace}name
表单传递给表单之前,应该使用命名空间字典对表单进行预处理findall()
.
Chr*_*isR 11
这是获取XML文档中所有命名空间的一种方法(假设没有前缀冲突).
我在解析XML文档时使用它,我事先知道命名空间URL是什么,只有前缀.
doc = etree.XML(XML_string)
# Getting all the name spaces.
nsmap = {}
for ns in doc.xpath('//namespace::*'):
if ns[0]: # Removes the None namespace, neither needed nor supported.
nsmap[ns[0]] = ns[1]
doc.xpath('//prefix:element', namespaces=nsmap)
Run Code Online (Sandbox Code Playgroud)
也许首先要注意的是命名空间是在元素级别而不是文档级别定义的.
但最常见的是,所有名称空间都在文档的根元素(office:document-content
此处)中声明,这样可以节省我们解析它以收集内部xmlns
作用域的所有名称空间.
然后元素nsmap包括:
None
前缀(并非总是)如果像ChrisR所提到的那样,不支持默认命名空间,则可以使用dict理解以更紧凑的表达式过滤掉它.
xpath和ElementPath的语法略有不同 .
所以这里是您可以用来获取所有第一个表的行的代码(使用以下测试lxml=3.4.2
):
import zipfile
from lxml import etree
# Open and parse the document
zf = zipfile.ZipFile('spreadsheet.ods')
tree = etree.parse(zf.open('content.xml'))
# Get the root element
root = tree.getroot()
# get its namespace map, excluding default namespace
nsmap = {k:v for k,v in root.nsmap.iteritems() if k}
# use defined prefixes to access elements
table = tree.find('.//table:table', nsmap)
rows = table.findall('table:table-row', nsmap)
# or, if xpath is needed:
table = tree.xpath('//table:table', namespaces=nsmap)[0]
rows = table.xpath('table:table-row', namespaces=nsmap)
Run Code Online (Sandbox Code Playgroud)