python使用xpath(超詳細(xì))

使用時(shí)先安裝 lxml 包

開始使用#

和beautifulsoup類似,首先我們需要得到一個(gè)文檔樹

把文本轉(zhuǎn)換成一個(gè)文檔樹對象

from lxml import etreeif __name__ == '__main__':doc='''

    first itemsecond itemthird itemfourth itemfifth item# 注意,此處缺少一個(gè)閉合標(biāo)簽
'''html = etree.HTML(doc)result = etree.tostring(html)print(str(result,'utf-8'))

把文件轉(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 --->

...
a? --->...

這里我們可以看到,這里的元素和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='''

'''html = etree.HTML(doc)print(html.xpath("http://li"))print(html.xpath("http://p"))

【結(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)簽
'''html = etree.HTML(doc)print(html.xpath("http://*[contains(@class,'item')]"))

【結(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='''

first itemsecond itemthird itemfourth itemfifth item# 注意,此處缺少一個(gè)閉合標(biāo)簽
'''html = etree.HTML(doc)print(html.xpath("http://*[contains(@class,'item')]"))print(html.xpath("http://*[starts-with(@class,'ul')]"))

【結(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='''

first itemsecond itemthird itemfourth itemfifth item# 注意,此處缺少一個(gè)閉合標(biāo)簽
'''html = etree.XML(doc)print(html.xpath("http://a/text()"))print(html.xpath("http://a")[0].text)print(html.xpath("http://ul")[0].text)print(len(html.xpath("http://ul")[0].text))print(html.xpath("http://ul/text()"))

【結(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='''

first itemsecond itemthird itemfourth itemfifth item# 注意,此處缺少一個(gè)閉合標(biāo)簽
'''html = etree.XML(doc)ns = etree.FunctionNamespace(None)ns['ends-with'] = ends_with #將ends_with方法注冊到方法命名空間中print(html.xpath("http://li[ends-with(@class,'active')]"))print(html.xpath("http://li[ends-with(@class,'active')]/a/text()"))

【結(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容