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属性。