【问题标题】:Handling nested elements with Python lxml使用 Python lxml 处理嵌套元素
【发布时间】:2014-10-14 20:52:35
【问题描述】:

鉴于下面的简单 XML 数据:

<book>
   <title>My First Book</title>
   <abstract>
         <para>First paragraph of the abstract</para>
         <para>Second paragraph of the abstract</para>
    </abstract>
    <keywordSet>
         <keyword>First keyword</keyword>
         <keyword>Second keyword</keyword>
         <keyword>Third keyword</keyword>
    </keywordSet>
</book>

如何使用lxml 遍历树,并获取“abstract”元素中的所有段落以及“keywordSet”元素中的所有关键字?

下面的代码 sn-p 只返回每个元素中的第一行文本:

from lxml import objectify
root = objectify.fromstring(xml_string) # xml_string contains the XML data above
print root.title # returns the book title
for line in root.abstract:
    print line.para # returns only yhe first paragraph
for word in root.keywordSet:
    print word.keyword # returns only the first keyword in the set

我尝试关注this example,但上面的代码没有按预期工作。

如果采用不同的策略,最好能够将整个 XML 树读入 Python 字典,其中每个元素作为键,每个文本作为元素项。我发现使用 lxml objectify 可能会实现这样的事情,但我不知道如何实现它。

我在尝试用 Python 编写 XML 解析代码时发现的一个非常大的问题是,提供的大多数“示例”都过于简单且完全是虚构的,无法提供太多帮助——否则它们正好相反,使用过于复杂的自动生成的 XML 数据!

谁能给我一个提示?

提前致谢!

编辑:发布此问题后,我找到了一个简单的解决方案here

所以,我更新后的代码变成了:

from lxml import objectify
    root = objectify.fromstring(xml_string) # xml_string contains the XML data above
    print root.title # returns the book title
    for para in root.abstract.iterchildren():
        print para # now returns the text of all paragraphs
    for keyword in root.keywordSet.iterchildren():
        print keyword # now returns all keywords in the set

【问题讨论】:

  • 确实,下面的@LukasGraf 答案也提供了同样的解决方案,但是以更 Python 的方式! :-)

标签: python xml lxml


【解决方案1】:

使用XPath 非常简单:

from lxml import etree

tree = etree.parse('data.xml')

paragraphs = tree.xpath('/abstract/para/text()')
keywords = tree.xpath('/keywordSet/keyword/text()')

print paragraphs
print keywords

输出:

['First paragraph of the abstract', 'Second paragraph of the abstract']
['First keyword', 'Second keyword', 'Third keyword']

有关 XPath 语法的详细信息,请参阅 the XPath Tutorial at W3Schools

特别是,上面表达式中使用的元素使用

  • / 选择器选择根节点/直接子节点。
  • text() 运算符用于选择各个元素的文本节点(“文本内容”)。

以下是使用 Objectify API 的方法:

from lxml import objectify

root = objectify.fromstring(xml_string)

paras = [p.text for p in root.abstract.para]
keywords = [k.text for k in root.keywordSet.keyword]

print paras
print keywords

看来root.abstract.para实际上是shorthandroot.abstract.para[0]。所以你需要显式使用element.iterchildren() 来访问所有子元素。

这不是真的,我们显然都误解了 Objectify API: 为了遍历abstract 中的paras,您需要遍历root.abstract.para,而不是root.abstract 本身。这很奇怪,因为您直观地将abstract 视为其节点的集合或容器,并且该容器将由 Python 可迭代表示。但实际上代表序列的是.para选择器。

【讨论】:

  • 当然,不错的提示!但是使用 lxml objectfy 是否也可以达到相同的结果?
  • 它会,毫无疑问。但是特别是对于您想要遍历树的(深度)嵌套结构,XPath 通常更加更容易和可读。
  • 嗯,有 objectify.ObjectPath,但提供的例子是,嗯,有点丑... :-(
  • @user40893 更新了我的答案,并举例说明了如何使用 Objectify API 来完成。
  • 请注意,尽管在您问题的解决方案中,para 实际上不是字符串,而是StringElement 对象。如果您打印它,它看起来像一个字符串,但根据您以后对它的处理,您可能需要使用 para.text 访问实际的字符串内容。
猜你喜欢
  • 1970-01-01
  • 2015-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 2016-07-01
相关资源
最近更新 更多