Python / ElementTree:跟随兄弟错误(在xpath测试器中工作)

时间:2016-12-31 15:08:01

标签: python xml xpath lxml elementtree

我有一个简单的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实现时原始问题存在的原因。

2 个答案:

答案 0 :(得分:2)

您应该使用xpath()方法:

for todo in root.xpath('//en-note//en-todo/following-sibling::text()[1]'):
    print todo

另请注意 - 我在开头添加了//并删除了.text - 您已经拥有了文本节点 - 他们没有{ {1}}属性。

答案 1 :(得分:1)

注意:此答案针对xml.etree.ElementTree。类似但更高级的lxml.etree模块具有完整的XPath支持,但下面显示的方法也适用于此。

直接来自the documentation,强调我的:

  

19.7.2。 XPath支持

     

此模块为XPath表达式提供有限的支持   在树中定位元素。 目标是支持一小部分   缩写语法;完整的XPath引擎超出了范围   模块。

您可以通过在Python中执行部分遍历来解决此问题。

在这种情况下,它特别容易,因为您可以使用方便tail property。其他情况需要更多工作。

parsed_note = ElementTree.fromstring(note_content)
for todo in parsed_note.findall('.//en-todo'):
    print todo.tail

您必须从返回的值中获得.strip()个空格。