使用Python的xml.etree查找元素开始和结束字符偏移量

时间:2011-11-13 12:36:41

标签: python xml sax elementtree

我有XML数据,如下所示:

<xml>
The captial of <place pid="1">South Africa</place> is <place>Pretoria</place>.
</xml>

我希望能够提取:

  1. 目前在etree中提供的XML元素。
  2. 文档的完整纯文本,位于开始和结束标记之间。
  3. 每个起始元素的纯文本中的位置,作为字符偏移。
  4. (3)是目前最重要的要求; etree提供(1)罚款。

    我无法直接看到任何方法(3),但希望迭代文档树中的元素会返回许多可以重新组合的小字符串,从而提供(2)和(3)。但是,请求根节点的.text仅返回根节点和第一个元素之间的文本,例如, “的首都 ”。

    使用SAX执行(1)可能涉及实施已经多次写入的批次,例如, minidom和etree。使用lxml不是此代码所涉及的包的选项。有人可以帮忙吗?

4 个答案:

答案 0 :(得分:4)

iterparse()功能在xml.etree中可用:

import xml.etree.cElementTree as etree

for event, elem in etree.iterparse(file, events=('start', 'end')):
    if event == 'start':
       print(elem.tag) # use only tag name and attributes here
    elif event == 'end':
       # elem children elements, elem.text, elem.tail are available
       if elem.text is not None and elem.tail is not None:
          print(repr(elem.tail))

另一种选择是覆盖start()的{​​{1}},data()end()方法:

etree.TreeBuilder()

输出

from xml.etree.ElementTree import XMLParser, TreeBuilder

class MyTreeBuilder(TreeBuilder):

    def start(self, tag, attrs):
        print("&lt;%s>" % tag)
        return TreeBuilder.start(self, tag, attrs)

    def data(self, data):
        print(repr(data))
        TreeBuilder.data(self, data)

    def end(self, tag):
        return TreeBuilder.end(self, tag)

text = """<xml>
The captial of <place pid="1">South Africa</place> is <place>Pretoria</place>.
</xml>"""

# ElementTree.fromstring()
parser = XMLParser(target=MyTreeBuilder())
parser.feed(text)
root = parser.close() # return an ordinary Element

答案 1 :(得分:1)

您需要查看.tail属性以及.text.text会在开始标记后直接为您提供文字,.tail会直接为您提供文字结束标记。这将为您提供“许多小字符串”。

提示:您可以使用etree.iterwalk(elem)(与etree.iterparse()完全相同,但在现有树上)来迭代开始和结束标记。这个想法:

for event, elem in etree.iterwalk(xml_elem, events=('start', 'end')):
    if event == 'start':
        # it's a start tag
        print 'starting element', elem.tag
        print elem.text
    elif event == 'end':
        # it's an end tag
        print 'ending element', elem.tag
        if elem is not xml_elem:
            # dont' want the text trailing xml_elem
            print elem.tail

我猜你可以自己完成剩下的工作吗? 警告:.text.tail可以是None,因此如果您想连接,则必须加以防范(例如,使用(elem.text or '')

如果您熟悉sax(或者现有的sax代码可以满足您的需求),lxml可以让您produce sax events from an element or tree

lxml.sax.saxify(elem, handler)

从元素中提取所有文本时要查找的其他一些事项:.itertext()方法,xpath表达式.//text()(lxml允许您从xpath表达式返回“智能字符串”:它们允许您检查他们属于哪个元素等...)。

答案 2 :(得分:0)

使用SAX很容易

(2),请参阅此片段

from xml.sax.handler import ContentHandler
import xml.sax
import sys

class textHandler(ContentHandler):
    def characters(self, ch):
        sys.stdout.write(ch.encode("Latin-1"))

parser = xml.sax.make_parser()
handler = textHandler()
parser.setContentHandler(handler)
parser.parse("test.xml")

或示例1-1:本书中的bookhandler.py http://oreilly.com/catalog/pythonxml/chapter/ch01.html

(3)比较棘手,请参考这个帖子,它是Java,但是在Python SAX api中应该有类似的东西How do I get the correct starting/ending locations of a xml tag with SAX?

答案 3 :(得分:0)

(3)可以使用XMLParser.CurrentByteIndex完成,如下所示:

import xml.etree.ElementTree as ET

class MyTreeBuilder(ET.TreeBuilder):
    def start(self, tag, attrs):
        print(parser.parser.CurrentByteIndex)
        ET.TreeBuilder.start(self, tag, attrs)

builder = MyTreeBuilder()
parser = ET.XMLParser(target=builder)
builder.parser = parser
tree = ET.parse('test.xml', parser=parser)

另请参阅this answer了解SAX替代方案。但请注意,字节索引与字符索引不同,并且可能没有一种在Python中将字节转换为字符索引的有效方法。 (另见here。)

获得字符偏移而不是字节偏移的一种(难以置信的丑陋)解决方法是将字节重新编码为字符。假设实际编码是utf8:

import xml.etree.ElementTree as ET

class MyTreeBuilder(ET.TreeBuilder):
    def start(self, tag, attrs):
        print(parser.parser.CurrentByteIndex)
        ET.TreeBuilder.start(self, tag, attrs)

builder = MyTreeBuilder()
parser = ET.XMLParser(target=builder)
builder.parser = parser
with open('test.xml', 'rb') as f:
    parser.feed(f.read().decode('latin1').encode('utf8'))