使用時(shí)先安裝 lxml 包
開始使用#
和beautifulsoup類似,首先我們需要得到一個(gè)文檔樹
把文本轉(zhuǎn)換成一個(gè)文檔樹對象
from lxml import etreeif __name__ == '__main__':doc='''
- first itemsecond itemthird itemfourth itemfifth item# 注意,此處缺少一個(gè)閉合標(biāo)簽
把文件轉(zhuǎn)換成一個(gè)文檔樹對象
fromlxmlimportetree# 讀取外部文件 index.htmlhtml = etree.parse('./index.html')result = etree.tostring(html, pretty_print=True)#pretty_print=True 會(huì)格式化輸出print(result)
均會(huì)打印出文檔內(nèi)容
節(jié)點(diǎn)、元素、屬性、內(nèi)容#
xpath 的思想是通過 路徑表達(dá) 去尋找節(jié)點(diǎn)。節(jié)點(diǎn)包括元素,屬性,和內(nèi)容
元素舉例
html --->...div --->
這里我們可以看到,這里的元素和html中的標(biāo)簽一個(gè)意思。單獨(dú)的元素是無法表達(dá)一個(gè)路徑的,所以單獨(dú)的元素不能獨(dú)立使用
路徑表達(dá)式#
/? 根節(jié)點(diǎn),節(jié)點(diǎn)分隔符,//? 任意位置.? 當(dāng)前節(jié)點(diǎn)..? 父級節(jié)點(diǎn)@? 屬性
通配符#
*? 任意元素@*? 任意屬性node()? 任意子節(jié)點(diǎn)(元素,屬性,內(nèi)容)
謂語#
使用中括號來限定元素,稱為謂語
//a[n] n為大于零的整數(shù),代表子元素排在第n個(gè)位置的元素//a[last()]? last()? 代表子元素排在最后個(gè)位置的元素//a[last()-]? 和上面同理,代表倒數(shù)第二個(gè)//a[position()<3] 位置序號小于3,也就是前兩個(gè),這里我們可以看出xpath中的序列是從1開始//a[@href]? ? 擁有href的元素//a[@href='www.baidu.com']? ? href屬性值為'www.baidu.com'的元素//book[@price>2]? price值大于2的元素
多個(gè)路徑#
用|?連接兩個(gè)表達(dá)式,可以進(jìn)行?或匹配
//book/title | //book/price
函數(shù)#
xpath內(nèi)置很多函數(shù)。更多函數(shù)查看https://www.w3school.com.cn/xpath/xpath_functions.asp
contains(string1,string2)
starts-with(string1,string2)
ends-with(string1,string2) #不支持
upper-case(string) #不支持
text()
last()
position()
node()
可以看到last()也是個(gè)函數(shù),在前面我們在謂語中已經(jīng)提到過了
案例#
定位元素#
匹配多個(gè)元素,返回列表
fromlxmlimportetreeif__name__ =='__main__':doc='''
- first item
- second item
- third item
- fourth item
- fifth item # 注意,此處缺少一個(gè) 閉合標(biāo)簽
【結(jié)果為】
[<Element li at 0x2b41b749848>, <Element li at 0x2b41b749808>, <Element li at 0x2b41b749908>, <Element li at 0x2b41b749948>, <Element li at 0x2b41b749988>][]? #沒找到p元素
html = etree.HTML(doc)print(etree.tostring(html.xpath("http://li[@class='item-inactive']")[0]))print(html.xpath("http://li[@class='item-inactive']")[0].text)print(html.xpath("http://li[@class='item-inactive']/a")[0].text)print(html.xpath("http://li[@class='item-inactive']/a/text()"))print(html.xpath("http://li[@class='item-inactive']/.."))print(html.xpath("http://li[@class='item-inactive']/../li[@class='item-0']"))
【結(jié)果為】
b'third item\n? ? ? ? ? ? ? ? 'None? ? #因?yàn)榈谌齻€(gè)li下面沒有直接text,Nonethird item? #['third item'][<Element ul at 0x19cd8c4c848>][<Element li at 0x15ea3c5b848>, <Element li at 0x15ea3c5b6c8>]
使用函數(shù)#
contains#
有的時(shí)候,class作為選擇條件的時(shí)候不合適@class='....'?這個(gè)是完全匹配,當(dāng)王爺樣式發(fā)生變化時(shí),class或許會(huì)增加或減少像active的class。用contains就能很方便
from lxml import etreeif __name__ == '__main__':doc='''
- first itemsecond itemthird itemfourth itemfifth item# 注意,此處缺少一個(gè)閉合標(biāo)簽
【結(jié)果為】
[<Element p at 0x23f4a9d12c8>, <Element li at 0x23f4a9d13c8>, <Element li at 0x23f4a9d1408>, <Element li at 0x23f4a9d1448>, <Element li at 0x23f4a9d1488>]
starts-with#
from lxml import etreeif __name__ == '__main__':doc='''
【結(jié)果為】
[<Element ul at 0x23384e51148>, <Element p at 0x23384e51248>, <Element li at 0x23384e51288>, <Element li at 0x23384e512c8>, <Element li at 0x23384e51308>, <Element li at 0x23384e51388>][<Element ul at 0x23384e51148>]
ends-with#
print(html.xpath("http://*[ends-with(@class,'ul')]"))
【結(jié)果為】
Traceback (most recent call last):File"F:/OneDrive/pprojects/shoes-show-spider/test/xp5_test.py",line18,inprint(html.xpath("http://*[ends-with(@class,'ul')]"))File"src\lxml\etree.pyx",line1582,inlxml.etree._Element.xpathFile"src\lxml\xpath.pxi",line305,inlxml.etree.XPathElementEvaluator.__call__File"src\lxml\xpath.pxi",line225,inlxml.etree._XPathEvaluatorBase._handle_resultlxml.etree.XPathEvalError: Unregisteredfunction
看來python的lxml并不支持有的xpath函數(shù)列表
upper-case#
和ends-with函數(shù)一樣,也不支持。同樣報(bào)錯(cuò)lxml.etree.XPathEvalError: Unregistered function
print(html.xpath("http://a[contains(upper-case(@class),'ITEM-INACTIVE')]"))
text、last#
#最后一個(gè)li被限定了print(html.xpath("http://li[last()]/a/text()"))#會(huì)得到所有的`<a>`元素的內(nèi)容,因?yàn)槊總€(gè)<a>標(biāo)簽都是各自父元素的最后一個(gè)元素。#本來每個(gè)li就只有一個(gè)<a>子元素,所以都是最后一個(gè)print(html.xpath("http://li/a[last()]/text()"))print(html.xpath("http://li/a[contains(text(),'third')]"))
【結(jié)果為】
['fifth item']['second item', 'third item', 'fourth item', 'fifth item'][<Element a at 0x26ab7bd1308>]
position#
print(html.xpath("http://li[position()=2]/a/text()"))#結(jié)果為['third item']
上面這個(gè)例子我們之前以及講解過了
*這里有個(gè)疑問,就是position()函數(shù)能不能像text()那樣用呢
print(html.xpath("http://li[last()]/a/position()"))#結(jié)果? lxml.etree.XPathEvalError: Unregisteredfunction
這里我們得到一個(gè)結(jié)論,函數(shù)不是隨意放在哪里都能得到自己想要的結(jié)果
node#
返回所有子節(jié)點(diǎn),不管這個(gè)子節(jié)點(diǎn)是什么類型(熟悉,元素,內(nèi)容)
print(html.xpath("http://ul/li[@class='item-inactive']/node()"))print(html.xpath("http://ul/node()"))
【結(jié)果為】
[]['\n? ? ? ? ? ? ? ? ', , '\n? ? ? ? ? ? ? ? ', , '\n? ? ? ? ? ? ? ? ', , '\n? ? ? ? ? ? ? ? ', , '\n? ? ? ? ? ? ? ? ', , ' 閉合標(biāo)簽\n? ? ? ? ? ? ']
獲取內(nèi)容#
**剛剛已經(jīng)提到過,可以使用.text和text()的方式來獲取元素的內(nèi)容
from lxml import etreeif __name__ == '__main__':doc='''
【結(jié)果為】
['first item','second item','third item','fourth item','fifth item']first item18['\n? ? ? ? ? ? ? ? ','\n? ? ? ? ? ? ? ? ','\n? ? ? ? ? ? ? ? ','\n? ? ? ? ? ? ? ? ','\n? ? ? ? ? ? ? ? ',' 閉合標(biāo)簽\n? ? ? ? ? ? ']
看到這里,我們觀察到text()和.text的區(qū)別。自己總結(jié)吧。不太好表達(dá),就不表達(dá)了
獲取屬性#
print(html.xpath("http://a/@href"))print(html.xpath("http://li/@class"))
【結(jié)果為】
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']['item-0active', 'item-1', 'item-inactive', 'item-1', 'item-0']
自定義函數(shù)#
我們從使用函數(shù)的過程中得到結(jié)論,就是有的函數(shù)不支持,有的支持,那問題來了,到底那些方法支持呢。我們在lxml官網(wǎng)找到了答案。https://lxml.de/xpathxslt.html。lxml 支持XPath 1.0 ,想使用其他擴(kuò)展,使用libxml2,和libxslt的標(biāo)準(zhǔn)兼容的方式。XPath 1.0官方文檔?以及其他版本的XPath文檔?https://www.w3.org/TR/xpath/
lxml supports XPath1.0, XSLT1.0andthe EXSLT extensions through libxml2andlibxsltina standards compliant way.
除此之外,lxml還提供了自定義函數(shù)的方式來擴(kuò)展xpath的支持度?https://lxml.de/extensions.html
from lxml import etree#定義函數(shù)def ends_with(context,s1,s2):return s1[0].endswith(s2)if __name__ == '__main__':doc='''
【結(jié)果為】
[<Element li at 0x2816ed30548>, <Element li at 0x2816ed30508>]['first item', 'third item']
形參s1會(huì)傳入xpath中的第一個(gè)參數(shù)@class,但這里注意@class是個(gè)列表
形參s2會(huì)傳入xpath中的第二個(gè)參數(shù)'active','active'是個(gè)字符串
官網(wǎng)例子https://lxml.de/extensions.html
defhello(context, a):return"Hello %s"% afromlxmlimportetreens = etree.FunctionNamespace(None)ns['hello'] = helloroot = etree.XML('<a><b>Haegar</b></a>')print(root.xpath("hello('Dr. Falken')"))# 結(jié)果為 Hello Dr. Falken