更新時(shí)間:2021-06-21 來源:黑馬程序員 瀏覽量:
lxml是使用Python語(yǔ)言編寫的庫(kù),主要用于解析和提取HTML或者XML格式的數(shù)據(jù),它不僅功能非常豐富,而且便于使用,可以利用XPath語(yǔ)法快速地定位特定的元素或節(jié)點(diǎn)。
lxml庫(kù)中大部分功能都位于lxml.etree模塊中,導(dǎo)入lxml.etree模塊的常見方式如下:
from lxml import etree
lxml庫(kù)的一些相關(guān)類如下:
(1) Element類:可以理解為XML的節(jié)點(diǎn)。
(2) ElementTree類:可以理解為一個(gè)完整的XML文檔樹。
(3) ElementPath類:可以理解為XPath,用于搜索和定位節(jié)點(diǎn)。
1.Element 類簡(jiǎn)介
Element類是XML處理的核心類,可以直觀地理解為XML的節(jié)點(diǎn),大部分XML節(jié)點(diǎn)的處理都是圍繞著Element類進(jìn)行的。要想創(chuàng)建一個(gè)節(jié)點(diǎn)對(duì)象,則可以通過構(gòu)造函數(shù)直接創(chuàng)建。例如:
root=etree.Element('root')
上述示例中,參數(shù)root表示節(jié)點(diǎn)的名稱。 關(guān)于Element類的相關(guān)操作,主要可分為三部分,分別是節(jié)點(diǎn)操作、節(jié)點(diǎn)屬性的操作、節(jié)點(diǎn)內(nèi)文本的操作,下面進(jìn)行逐一介紹。 (1)節(jié)點(diǎn)操作:若要獲取節(jié)點(diǎn)的名稱,可以通過tag屬性獲取。例如:
print(root.tag) # 輸出結(jié)果如下 root
(2)節(jié)點(diǎn)屬性的操作:在創(chuàng)建節(jié)點(diǎn)的同時(shí),可以為節(jié)點(diǎn)增加屬性。節(jié)點(diǎn)中的屬性是以key-value的形式進(jìn)行存儲(chǔ)的,類似于字典的存儲(chǔ)方式。通過構(gòu)造方法創(chuàng)建節(jié)點(diǎn)時(shí),可以在該方法中以參數(shù)的形式設(shè)置屬性,其中參數(shù)的名稱表示屬性的名稱,參數(shù)的值表示為屬性的值。創(chuàng)建屬性的示例如下:
# 創(chuàng)建root節(jié)點(diǎn),并為其添加屬性 root=etree.Element('root', interesting='totally') print(etree.tostring(root)) # 輸出結(jié)果如下 b'<root interesting=" totally" />'
此外,可以通過set()方法給已有的節(jié)點(diǎn)添加屬性。在調(diào)用該方法時(shí)可以傳入兩個(gè)參數(shù),其中第一個(gè)參數(shù)表示屬性的名稱,第二個(gè)參數(shù)表示屬性的值。例如:
# 再次給root節(jié)點(diǎn)添加age屬性 root.set('age', '30') print(etree.tostring(root)) # 輸出結(jié)果如下 b'<root interesting="totally"age="30"/>'
在上述兩個(gè)示例中,都用到了tostring()函數(shù),該函數(shù)可以將元素序列化為XML樹的編碼字符串表示形式。
(3)節(jié)點(diǎn)內(nèi)文本的操作:一般情況下,可以通過text、tail屬性或者xpath()方法來訪問文本內(nèi)容。通過text屬性訪問節(jié)點(diǎn)的示例如下:
root=etree.Element('root') # 創(chuàng)建root節(jié)點(diǎn) root.text='Hello, World!' # 給root節(jié)點(diǎn)添加文本 print(root.text) print(etree.tostring(root)) # 輸出結(jié)果如下 Hello, world! b'<root>Hello, World!</root>'
2.從字符串或文件中解析XML
為了能夠?qū)ML文件解析為樹結(jié)構(gòu),etree模塊中提供了如下3個(gè)函數(shù): (1 ) fromstring()函數(shù):從字符串中解析XML文檔或片段,返回根節(jié)點(diǎn)(或解析器目標(biāo)返回的結(jié)果)。 (2) XML()函數(shù):從字符串常量中解析XML文檔或片段,返回根節(jié)點(diǎn)(或解析器目標(biāo)返回的結(jié)果)。 (3) HTML()函數(shù):從字符串常量中解析HTML文檔或片段,返回根節(jié)點(diǎn)(或解析器目標(biāo)返回的結(jié)果)。 其中,XML()函數(shù)的行為類似于fromstring0函數(shù),通常用于將XML字面量直接寫入到源代碼中;HTML()函數(shù)可以自動(dòng)補(bǔ)全缺少的<html>和<body>標(biāo)簽。以上3個(gè)函數(shù)的示例如下:
xml_data='<root>data</root>' # fromstring()方法 root_one=etree.fromstring(xml_data) print(root_one.tag) print(etree.tostring(root_one)) # XML方法,與fromstring方法基本一樣 root_two=etree.XML(xml_data) print(root_two.tag) print(etree.tostring(root_two)) # HTML()方法,如果沒有<html>和<body>標(biāo)簽,會(huì)自動(dòng)補(bǔ)上 root_three=etree.HTML(xml_data) print(root_three.tag) print(etree.tostring(root_three)) 程序運(yùn)行結(jié)果為: root b'<root>data</root>' root b'<root>data</root>' html b'<html><body><root>data</root></body></html>'
除了上述3個(gè)函數(shù)之外,還可以調(diào)用parse()函數(shù)從XML文件中直接解析。在調(diào)用函數(shù)時(shí),如果沒有提供解析器,則使用默認(rèn)的解析器,函數(shù)會(huì)返回一個(gè)ElemenfTree 類的對(duì)象。例如:
html=etree.parse('./hello.html') result=etree.tostring(html, pretty_print=True)
ElementPath類簡(jiǎn)介
ElementTree類中附帶了一個(gè)類似于XPath路徑語(yǔ)言的ElementPath類。在ElementTree類或Elements類的API文檔中,提供了3個(gè)常用的方法,可以滿足大部分搜索和查詢需求,并且這3個(gè)方法的參數(shù)都是XPath語(yǔ)句。具體如下: (1) find()方法:返回匹配到的第一 個(gè)子元素。 (2) findall()方法:以列表的形式返回所有匹配的子元素。 (3) iterfind()方法:返回一個(gè)所有匹配元素的迭代器。 從文檔樹的根節(jié)點(diǎn)開始,搜索符合要求的節(jié)點(diǎn)。例如:
# 從字符串中解析XML,返回根節(jié)點(diǎn) root=etree.XML("<root><a x='123'>aText<b/><c/><b/></a></root>") # 從根節(jié)點(diǎn)查找,返回匹配到的節(jié)點(diǎn)名稱 print(root.find("a").tag) # 從根節(jié)點(diǎn)開始查找,返回匹配到的第一個(gè)節(jié)點(diǎn)的名稱 print(root.findall(".//a[@x]")[0].tag)
程序運(yùn)行結(jié)果為:
a A
還可以調(diào)用xpath()方法,使用元素作為上下文節(jié)點(diǎn)來評(píng)估XPath表達(dá)式。
lxml庫(kù)的基本使用
這里使用一個(gè)HTML示例文件作為素材來介紹lxml庫(kù)的基本應(yīng)用。該文件名為hello.html,內(nèi)容如下:
<!-- hello.html --> <div> <ul> <li class="item-0"><a href="link1.html">first item</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div>
按下來,基于上述HTML文檔,使用lxml庫(kù)中的路徑表達(dá)式技巧,通過調(diào)用xpath()方法匹配選取的節(jié)點(diǎn),具體如下:
獲取任意位置的li節(jié)點(diǎn) 可以直接使用“//”從任意位置選取節(jié)點(diǎn)li,路徑表達(dá)式如下:
//li
通過lxml.etree模塊的xpath()方法,將hello.html文件中與該路徑表達(dá)式匹配到的列表返回,并打印輸出。具體代碼如下:
from lxml import etree html=etree.parse('hello.html') # 查找所有的li節(jié)點(diǎn) result=html.xpath('//li') # 打印<li>標(biāo)簽的元素集合 print(result) # 打印<li>標(biāo)簽的個(gè)數(shù) print(len(result)) # 打印返回結(jié)果的類型 print(type(result)) # 打印第一個(gè)元素的類型 print(type(result[0]))
程序運(yùn)行結(jié)果為:
[<Element li at 0x2cc9a48>, <Element li at 0x2cc99c8>, <Element li at 0x2cc9a88>, <Element li at 0x2cc9ac8>, <Element li at 0x2cc9b08>] 5 <class 'list'> <class 'lxml.etree._Element'>
繼續(xù)獲取<li>標(biāo)簽的class屬性
在上個(gè)表達(dá)式的末尾,使用“/”向下選取節(jié)點(diǎn),并使用@選取class屬性節(jié)點(diǎn),表達(dá)式如下:
//1i/@class
獲取<li>標(biāo)簽的class屬性的示例代碼如下:
from lxml import etree html=etree.parse('hello.html') # 查找位于li標(biāo)簽的class屬性 result=html.xpath('//li/@class') print(result)
程序運(yùn)行結(jié)果為:
['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']
獲取倒數(shù)第二個(gè)元素的內(nèi)容
從任意位置開始選取倒數(shù)第二個(gè)<li>標(biāo)簽,再向下選取標(biāo)簽<a>。如果要獲取該標(biāo)簽中的 文本,可以使用如下表達(dá)式:
//li[last()-1]/a
或者
//li[last()-1]/a]/text()
不同的是,第個(gè)表達(dá)式需要訪問text屬性,才能拿到標(biāo)簽的文本,而第二個(gè)表達(dá)式可直 接獲取文本。使用第一 個(gè)路徑表達(dá)式的示例如下:
from lxml import etree html=etree.parse('hello.html') # 獲取倒數(shù)第二個(gè)元素的內(nèi)容 result=html.xpath('//li[last()-1]/a') print(result[0].text)
程序運(yùn)行結(jié)果:
fourth item