Etree 支持從所有重要來源(即字符串、文件、 url (http / ftp)和類似文件的對象)以多種方式解析 XML。 主要的解析函數是 fromstring ()和 parse () ,它們都是以 source 作爲第一個參數來調用的。 默認情況下,它們使用標準的解析器,但是您總是可以將不同的解析器作爲第二個參數傳遞。
(1)The fromstring() function
函數是解析字符串最簡單的方法:
>>> some_xml_data = "<root>data</root>"
>>> root = etree.fromstring(some_xml_data)
>>> print(root.tag)
root
>>> etree.tostring(root)
b'<root>data</root>
(2)The XML() function
函數的行爲類似於 fromstring ()函數,但通常用於將 XML 文本直接寫入源代碼中
>>> root = etree.XML("<root>data</root>")
>>> print(root.tag)
root
>>> etree.tostring(root)
b'<root>data</root>'
Html 文本還有一個相應的函數 HTML ()。
>>> root = etree.HTML("<p>data</p>")
>>> etree.tostring(root)
b'<html><body><p>data</p></body></html>'
(3)The parse() function
作爲這種類文件對象的示例,下面的代碼使用 BytesIO 類從字符串而不是外部文件進行讀取。 這個類來自 Python 2.6及更高版本中的 io 模塊。 在較早的 Python 版本中,必須使用 StringIO 模塊中的 StringIO 類。 但是,在現實生活中,您顯然應該避免一起執行這些操作,而是使用上面提到的字符串解析函數。
>>> from io import BytesIO
>>> some_file_or_file_like_object = BytesIO(b"<root>data</root>")
>>> tree = etree.parse(some_file_or_file_like_object)
>>> etree.tostring(tree)
b'<root>data</root>'
注意 parse ()返回一個 ElementTree 對象,而不是作爲字符串解析器函數的 Element 對象:
>>> root = tree.getroot()
>>> print(root.tag)
root
>>> etree.tostring(root)
b'<root>data</root>'
這種差異背後的原因是 parse ()從文件返回一個完整的文檔,而字符串解析函數通常用於解析 XML 片段。
函數支持以下任何來源:
- an open file object (make sure to open it in binary mode) 一個打開的文件對象(確保以二進制模式打開它)
- a file-like object that has a 一個類似文件的對象.read(byte_count) . read (字節計數) method returning a byte string on each call 方法在每次調用時返回一個字節字符串
- a filename string 文件名字符串
- an HTTP or FTP URL string 一個 HTTP 或 FTP URL 字符串
注意,傳遞文件名或 URL 通常比傳遞打開的文件或類似文件的對象更快。 然而,libxml2中的 HTTP / ftp 客戶端相當簡單,因此像 HTTP 身份驗證這樣的事情需要一個專用的 URL 請求庫,例如 urllib2或請求。 這些庫通常爲結果提供一個類似於文件的對象,當響應流輸入時可以從中進行分析。
(4)解析器對象(Parser objects)
默認情況下,lxml.etree 使用默認設置的標準解析器。 如果你想配置解析器,你可以創建一個新的實例:
>>> parser = etree.XMLParser(remove_blank_text=True) # lxml.etree only!
這將創建一個解析器,在解析時移除標記之間的空文本,這樣可以減小樹的大小,並避免在知道純空格內容對數據沒有意義時懸空尾文本。 舉個例子:
>>> root = etree.XML("<root> <a/> <b> </b> </root>", parser)
>>> etree.tostring(root)
b'<root><a/><b> </b></root>'
注意,b 標記中的空白內容沒有被刪除,因爲 leaf 元素中的內容往往是數據內容(即使是空白)。 你可以通過遍歷這棵樹來輕鬆地移除它:
>>> for element in root.iter("*"):
... if element.text is not None and not element.text.strip():
... element.text = None
>>> etree.tostring(root)
b'<root><a/><b/></root>
查看 help (etree.XMLParser)以瞭解可用的解析器選項。
(5)增量解析(Parser objects)
Etree 提供了兩種逐步增量解析的方法。 一種是通過類似於文件的對象,其中它反覆調用 read ()方法。 這最好用於數據從諸如 urllib 這樣的源或任何其他類似文件的對象(可以根據請求提供數據)到達的地方。 注意,在這種情況下,解析器會阻塞並等待,直到數據可用:
>>> class DataSource:
... data = [ b"<roo", b"t><", b"a/", b"><", b"/root>" ]
... def read(self, requested_size):
... try:
... return self.data.pop(0)
... except IndexError:
... return b''
>>> tree = etree.parse(DataSource())
>>> etree.tostring(tree)
b'<root><a/></root>'
第二種方法是通過 feed 解析器接口,由 feed (data)和 close ()方法提供:
>>> parser = etree.XMLParser()
>>> parser.feed("<roo")
>>> parser.feed("t><")
>>> parser.feed("a/")
>>> parser.feed("><")
>>> parser.feed("/root>")
>>> root = parser.close()
>>> etree.tostring(root)
b'<root><a/></root>'
在這裏,您可以隨時中斷解析過程,稍後通過調用 feed ()方法繼續解析過程。 如果你想避免阻塞對解析器的調用,比如像 Twisted 這樣的框架,或者當數據緩慢或者大塊地進入時,你想在等待下一個數據塊的同時做其他事情,這就很方便了。
在調用 close ()方法之後(或者當解析器引發異常時) ,您可以通過再次調用解析器的 feed ()方法來重用解析器:
>>> parser.feed("<root/>")
>>> root = parser.close()
>>> etree.tostring(root)
b'<root/>'
(6)事件驅動的解析(Event-driven parsing)
有時候,您只需要從一個文檔中獲得一小部分內容,因此將整個樹解析到內存中、遍歷和刪除它的開銷可能太大。 Etree 通過兩個事件驅動的解析器接口支持這個用例,一個在構建樹(iterparse)時生成解析器事件,另一個根本不構建樹,而是以類似 sax 的方式對目標對象調用反饋方法。
下面是一個簡單的 iterparse ()示例:
>>> 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
請注意,接收開始事件時,Element 的文本、尾部和子元素不一定存在。 只有結束事件才能保證元素被完全解析。
它還允許你。 清除()或修改元素的內容以節省內存。 因此,如果您解析一個大的樹並希望保持內存使用量較小,那麼您應該清理樹中不再需要的部分。 保持尾部爲真的參數。 Clear ()確保當前元素後面的(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 構建樹,並且只截取這一個 Element,使用普通的樹 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