Python / ElementTree:跟随兄弟错误(在 xpath 测试器中工作)
Python / ElementTree: following-sibling error (working in xpath tester)
我有一个简单的 XML 文档(实际上是 Evernote 的 ENML)如下:
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note>
<div>Here is the Evernote logo:</div>
<div>
<en-media type="image/png" hash="a54fe8bcd146e20a8a5742834558543c" />
</div>
<div>
<br />
</div>
<div>
<en-todo />
Task 1
</div>
<div>making it a bit harder</div>
<div>
<en-todo />
Task 2 | 2016-12-31
</div>
<div>
<br />
</div>
<div>
This is another to-do
<en-todo />
in an awkward place
</div>
</en-note>
我正在尝试使用 Xpath 访问紧跟在 en-todo
标记之后的文本。我的代码是:
parsed_note = ElementTree.fromstring(note_content)
for todo in parsed_note.findall('en-note//en-todo/following-sibling::text()[1]'):
print todo.text
我已经在 freeformatter.com 使用 Xpath 测试器对此进行了测试 - 它似乎有效,但只有当我从 XML 中删除 <!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
标签时 - 我认为这是测试人员的怪癖。输出是:
Text='Task 1'
Text='Task 2 | 2016-12-31'
Text='in an awkward place'
这完全符合预期和期望。
当我尝试 运行 Python 中的代码时,我得到:SyntaxError: prefix 'following-sibling' not found in prefix map
.
我怀疑这可能与测试人员有相同的怪癖并删除了文件类型标签,但同样的错误仍然存在。
我正在使用标准解析器:
import defusedxml.lxml as lxml
from lxml import etree as ElementTree
我哪里出错了 - 是我的 xpath 语句有缺陷,还是我遗漏了其他原因?
编辑:@Tomalek 提供了一个有效的解决方案,使用 Python tail
函数而不是完整的 xpath。鉴于@alecxe 的评论,所引用的文档不适用于 lxml,我将保持开放状态,以防任何人想冒险了解为什么在应该有完整的 xpath 实现时存在原始问题。
注:本回答针对xml.etree.ElementTree
。类似但更高级的 lxml.etree
模块具有完整的 XPath 支持,但下面显示的方法也适用于此。
直接来自 the documentation,强调我的:
19.7.2. XPath support
This module provides limited support for XPath expressions for
locating elements in a tree. The goal is to support a small subset
of the abbreviated syntax; a full XPath engine is outside the scope of
the module.
您可以通过在 Python 中进行部分遍历来解决它。
在这种情况下特别容易,因为有一个方便的 tail
property 您可以使用。其他情况需要更多工作。
parsed_note = ElementTree.fromstring(note_content)
for todo in parsed_note.findall('.//en-todo'):
print todo.tail
您必须 .strip()
返回值中的空格。
你应该用过xpath()
方法:
for todo in root.xpath('//en-note//en-todo/following-sibling::text()[1]'):
print todo
另请注意 - 我在开头添加了 //
并删除了 .text
- 你已经有了文本节点 - 它们没有 .text
属性。
我有一个简单的 XML 文档(实际上是 Evernote 的 ENML)如下:
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note>
<div>Here is the Evernote logo:</div>
<div>
<en-media type="image/png" hash="a54fe8bcd146e20a8a5742834558543c" />
</div>
<div>
<br />
</div>
<div>
<en-todo />
Task 1
</div>
<div>making it a bit harder</div>
<div>
<en-todo />
Task 2 | 2016-12-31
</div>
<div>
<br />
</div>
<div>
This is another to-do
<en-todo />
in an awkward place
</div>
</en-note>
我正在尝试使用 Xpath 访问紧跟在 en-todo
标记之后的文本。我的代码是:
parsed_note = ElementTree.fromstring(note_content)
for todo in parsed_note.findall('en-note//en-todo/following-sibling::text()[1]'):
print todo.text
我已经在 freeformatter.com 使用 Xpath 测试器对此进行了测试 - 它似乎有效,但只有当我从 XML 中删除 <!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
标签时 - 我认为这是测试人员的怪癖。输出是:
Text='Task 1'
Text='Task 2 | 2016-12-31'
Text='in an awkward place'
这完全符合预期和期望。
当我尝试 运行 Python 中的代码时,我得到:SyntaxError: prefix 'following-sibling' not found in prefix map
.
我怀疑这可能与测试人员有相同的怪癖并删除了文件类型标签,但同样的错误仍然存在。
我正在使用标准解析器:
import defusedxml.lxml as lxml
from lxml import etree as ElementTree
我哪里出错了 - 是我的 xpath 语句有缺陷,还是我遗漏了其他原因?
编辑:@Tomalek 提供了一个有效的解决方案,使用 Python tail
函数而不是完整的 xpath。鉴于@alecxe 的评论,所引用的文档不适用于 lxml,我将保持开放状态,以防任何人想冒险了解为什么在应该有完整的 xpath 实现时存在原始问题。
注:本回答针对xml.etree.ElementTree
。类似但更高级的 lxml.etree
模块具有完整的 XPath 支持,但下面显示的方法也适用于此。
直接来自 the documentation,强调我的:
19.7.2. XPath support
This module provides limited support for XPath expressions for locating elements in a tree. The goal is to support a small subset of the abbreviated syntax; a full XPath engine is outside the scope of the module.
您可以通过在 Python 中进行部分遍历来解决它。
在这种情况下特别容易,因为有一个方便的 tail
property 您可以使用。其他情况需要更多工作。
parsed_note = ElementTree.fromstring(note_content)
for todo in parsed_note.findall('.//en-todo'):
print todo.tail
您必须 .strip()
返回值中的空格。
你应该用过xpath()
方法:
for todo in root.xpath('//en-note//en-todo/following-sibling::text()[1]'):
print todo
另请注意 - 我在开头添加了 //
并删除了 .text
- 你已经有了文本节点 - 它们没有 .text
属性。