事件驅動解析

有時咱們須要的只是樹內某個地方的一小部分,所以將整個樹解析到內存中,全體遍歷並進行相關操做形成的內存開銷會很大
所以,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關鍵字參數控制app

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)文本內容不會被觸摸
強烈建議修改解析器可能還沒有徹底讀取的任何內容spa

 

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來提取數據code

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的事件。經過實現部分或所有這些方法,能夠控制生成哪些事件xml

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
''' 
相關文章
相關標籤/搜索