点击此处浏览总目录

事件驱动解析

有时我们需要的只是树内某个地方的一小部分,因此将整个树解析到内存中,全体遍历并进行相关操作造成的内存开销会很大
因此,etree提供了两个事件驱动的解析器接口
一个是在构建树时,生成解析器事件(iterparse)
另一个根本不构建树,而是以类似SAX的方式调用目标对象的反馈方法

 

some_file_like = BytesIO(b"<root><a>data</a></root>")
for event, element in etree.iterparse(some_file_like):
    print("%s, %4s, %s" % (event, element.tag, element.text)
    
'''
输出:
end,    a, data
end, root, None
'''

 

默认情况下,iterparse()只在解析完元素时生成事件,但可以通过events关键字参数控制

some_file_like = BytesIO(b"<root><a>data</a></root>")
for event, element in etree.iterparse(some_file_like,events=("start", "end")):
    print("%5s, %4s, %s" % (event, element.tag, element.text))
    
'''
输出:
start, root, None
start,    a, data
  end,    a, data
  end, root, None
'''  

注意,在接收开始事件时,元素的文本、尾部和子元素还不一定存在,只有结束事件才能保证元素已被完全解析
它还允许您.clear()或修改元素的内容以节省内存
因此,如果您解析一个大的树并且希望保持内存使用量小,那么您应该清理树中不再需要的部分
.clear()的keep_tail=True参数确保当前元素后面的(tail)文本内容不会被触摸
强烈建议修改解析器可能尚未完全读取的任何内容

 

some_file_like = BytesIO(b"<root><a><b>data</b></a><a><b/></a></root>")
for event, element in etree.iterparse(some_file_like):
    if element.tag == 'b':
        print(element.text)
    elif element.tag == 'a':
        print("** cleaning up the subtree")
        element.clear(keep_tail=True)
'''
输出:  
data
** cleaning up the subtree
None
** cleaning up the subtree
'''  

 

 

iterparse()的一个非常重要的用例是解析生成的大型XML文件,例如数据库转储
大多数情况下,这些XML格式只有一个主数据项元素挂在根节点的正下方,并且重复了数千次
在这种情况下,最好让lxml.etree来构建树,并且只截取这一个元素,使用普通的树API来提取数据

xml_file = BytesIO(b'''\
    <root>
        <a><b>ABC</b><c>abc</c></a>
        <a><b>MORE DATA</b><c>more data</c></a>
        <a><b>XYZ</b><c>xyz</c></a>
    </root>''')

for _, element in etree.iterparse(xml_file, tag='a'):
    print('%s -- %s' % (element.findtext('b'), element[1].text))
    element.clear(keep_tail=True)

'''
输出:  
ABC -- abc
MORE DATA -- more data
XYZ -- xyz
'''  

 

如果出于某种原因根本不需要构建树,则可以使用lxml.etree的目标解析器接口
它通过调用目标对象的方法创建类似SAX的事件。通过实现部分或全部这些方法,可以控制生成哪些事件

class ParserTarget:
     events = []
     close_count = 0
     def start(self, tag, attrib):
         self.events.append(("start", tag, attrib))
     def close(self):
         events, self.events = self.events, []
         self.close_count += 1
         return events

parser_target = ParserTarget()

parser = etree.XMLParser(target=parser_target)
events = etree.fromstring('<root test="true"/>', parser)

print(parser_target.close_count) #输出:1

for event in events:
     print('event: %s - tag: %s' % (event[0], event[1]))
     for attr, value in event[2].items():
         print(' * %s = %s' % (attr, value))
         
'''
输出:     
event: start - tag: root
 * test = true
''' 

 

可随时重用解析器及其目标,因此要确保.close()方法确实已将目标状态重置为可用(在出现错误时也是如此!)

events = etree.fromstring('<root test="true"/>', parser)
print(parser_target.close_count) #输出:2

events = etree.fromstring('<root test="true"/>', parser)
print(parser_target.close_count) #输出:3

events = etree.fromstring('<root test="true"/>', parser)
print(parser_target.close_count) #输出:4

for event in events:
     print('event: %s - tag: %s' % (event[0], event[1]))
     for attr, value in event[2].items():
         print(' * %s = %s' % (attr, value))
         
'''
输出: 
event: start - tag: root
 * test = true
''' 

 

 

 

posted @ 2019-11-11 16:18  立业的博客  阅读(278)  评论(0编辑  收藏  举报