xpath中两个冒号_爬虫学习(5)—XPath
之前我們寫了一個(gè)簡(jiǎn)單的爬蟲,在提取頁面信息時(shí)我們使用正則表達(dá)式來匹配內(nèi)容,但是正則表達(dá)式的書寫比較繁瑣,而且一旦錯(cuò)誤就可能導(dǎo)致匹配失敗。對(duì)于網(wǎng)頁的節(jié)點(diǎn)來說,它可以定義id,class或其他的屬性,而且節(jié)點(diǎn)之間還有層次關(guān)系,在網(wǎng)頁中可以通過xpath后css選擇器來定位一個(gè)或多個(gè)節(jié)點(diǎn)。那么,我們?cè)诮馕鲰撁鏁r(shí),利用CSS和XPath選擇器來定位節(jié)點(diǎn),再調(diào)用相關(guān)方法來獲取其正文內(nèi)容或?qū)傩浴1疚慕榻BXPath。
1.XPath概述
XPath的選擇功能十分強(qiáng)大,提供了100多個(gè)內(nèi)建函數(shù),用于字符串、數(shù)值、時(shí)間的匹配和節(jié)點(diǎn)、序列的處理。
2.XPath常用規(guī)則
nodename 選取此節(jié)點(diǎn)的所有子節(jié)點(diǎn) / 從當(dāng)前節(jié)點(diǎn)選取直接子節(jié)點(diǎn) // 從當(dāng)前節(jié)點(diǎn)選取子孫節(jié)點(diǎn) . 選取當(dāng)前節(jié)點(diǎn) .. 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) @ 選取屬性這里就是XPath的常用匹配規(guī)則,示例如下:
//title[@lang='eng']
這個(gè)就表示選擇所有名稱為title,同時(shí)屬性lang的值為eng的節(jié)點(diǎn)。
3.準(zhǔn)備工作
安裝好lxml庫。
4.實(shí)例引入
我們來通過實(shí)例來感受一下使用XPath來對(duì)網(wǎng)頁進(jìn)行解析的過程。
from lxml import etreetext = ''' <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">third item</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> </ul> </div> ''' html = etree.HTML(text) result = etree.tostring(html) print(result.decode('utf-8'))這里我們先導(dǎo)入lxml庫的etree模塊,然后聲明一段HTML文本,在調(diào)用HTML類進(jìn)行初始化,這樣就成功構(gòu)造了一個(gè)XPath解析對(duì)象。我們可以注意到HTML文本中最后一個(gè)li節(jié)點(diǎn)是沒有閉合的,但是etree模塊可以自動(dòng)修正HTML 文本。
我們調(diào)用tostring()方法即可輸出修正后的HTML代碼,但是結(jié)果是bytes類型。這里利用decode()方法將其轉(zhuǎn)換為str類型。
我們也可以通過直接讀取文件來進(jìn)行解析。
from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser()) result = etree.tostring(html) print(result.decode('utf-8'))其中test.html的內(nèi)容就是上面的HTML代碼。
5.所有節(jié)點(diǎn)
我們一般會(huì)用//開頭的XPath規(guī)則來選取所有符合要求的節(jié)點(diǎn)。
result = html.xpath('//*') print(result)這里使用*來匹配所有節(jié)點(diǎn),也就是整個(gè)HTML文本中的所有節(jié)點(diǎn)都會(huì)被獲取,返回形式是一個(gè)列表,每個(gè)元素都是Element類型,后面跟著節(jié)點(diǎn)的名稱。
當(dāng)然,此處匹配也可以指定節(jié)點(diǎn)名稱。如果想獲取所有l(wèi)i節(jié)點(diǎn),如下:
result = html.xpath('//li') print(result) print(result[0])如果我們要取出其中一個(gè)對(duì)象,可以直接用中括號(hào)加索引,如[0]。
6.子節(jié)點(diǎn)
我們通過/或//即可查找元素的子節(jié)點(diǎn)或子孫節(jié)點(diǎn)。假設(shè)我們現(xiàn)在想選擇li節(jié)點(diǎn)的所有直接a節(jié)點(diǎn),可以這樣實(shí)現(xiàn):
result = html.xpath('//li/a')如果想獲得所有子孫節(jié)點(diǎn),則可使用//。例如,要獲取ul節(jié)點(diǎn)下的所有子孫a節(jié)點(diǎn),可以這樣實(shí)現(xiàn):
result = html.xpath('//ul//a')/用于獲取直接子節(jié)點(diǎn),//用于獲取子孫節(jié)點(diǎn)。
7.父節(jié)點(diǎn)
我們?cè)谏厦嬷v了如何查找子節(jié)點(diǎn)或子孫節(jié)點(diǎn),那么加入我們知道了子節(jié)點(diǎn),怎么來查找父節(jié)點(diǎn)呢?可以用..來實(shí)現(xiàn)。
比如,我們選中屬性為link4.html的a節(jié)點(diǎn),然后獲取其父節(jié)點(diǎn),然后再獲取其class屬性。
result = html.xpath('//a[@href="link.html"]/../@class')同時(shí)也可以使用parent::來獲取父節(jié)點(diǎn)。
result = html.xpath('//a[@href="link.html"]/parent::*/@class')8.屬性匹配
在選取時(shí),我們還可以使用@符號(hào)進(jìn)行屬性過濾。比如,這里如果要選取class為item-0的li節(jié)點(diǎn),可以這樣實(shí)現(xiàn):
result = html.xpath('//li[@class="item-0"]')這里我們通過加入[@class="item-0"],限制了節(jié)點(diǎn)的class屬性為item-0,而HTML 文本中符合的li節(jié)點(diǎn)有兩個(gè),所以結(jié)果應(yīng)該返回兩個(gè)匹配到的元素。
9.文本獲取
我們使用XPath中的text()方法獲取節(jié)點(diǎn)中的文本,接下來嘗試獲取前面li節(jié)點(diǎn)中的文本。
result = html.xpath('//li[@class="item-0"]/text()')我們運(yùn)行之后沒有獲取任何文本。這是為什么呢?因?yàn)閄Path中text()前面是/,代表選取直接子節(jié)點(diǎn),很明顯li節(jié)點(diǎn)的直接子節(jié)點(diǎn)都是a節(jié)點(diǎn),文本都是在a節(jié)點(diǎn)內(nèi)部的,所以只匹配到被修正的li節(jié)點(diǎn)內(nèi)部的換行符。因?yàn)樽詣?dòng)修正的li節(jié)點(diǎn)的尾標(biāo)簽換行了。即選中的是這兩個(gè)節(jié)點(diǎn):
<li class="item-0"><a-href="link1.html">first item</a></li> <li class="item-0"><a-href="link5.html">fifth item</a> </li>因此,如果想獲取li節(jié)點(diǎn)內(nèi)部的文本,就有兩種方式:一種是先選取a節(jié)點(diǎn)再獲取文本,另一種是使用//。
result = html.xpath('//li[@class="item-0"]/a/text()')運(yùn)行結(jié)果為:
['first item', 'fifth item']這里我們是逐層選取,先選取了li節(jié)點(diǎn),又利用/選取了其直接子節(jié)點(diǎn)a。
result = html.xpath('//li[@class="item-0"]//text()')運(yùn)行結(jié)果為:
['first item', 'fifth item', 'n ']這里是選取所有子孫節(jié)點(diǎn)的文本,其中兩個(gè)是li的子節(jié)點(diǎn)a節(jié)點(diǎn)內(nèi)部的文本,另外一個(gè)就是最后一個(gè)li節(jié)點(diǎn)內(nèi)部的文本,即換行符。
10.屬性獲取
我們使用text()方法獲取文本內(nèi)容,我們也可以用@href來獲取節(jié)點(diǎn)的href屬性。
result = html.xpath('//li/a/@href')11.屬性多值匹配
有時(shí)候,某些節(jié)點(diǎn)的某個(gè)屬性有多個(gè)值,例如:
from lxml import etreetext = ''' <li class="li li-first"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[@class="li"]/a/text()') print(result)這里返回的結(jié)果是[],因?yàn)閘i的class屬性有兩個(gè)值li和li-first,此時(shí)還想用之前的屬性匹配獲取就無法匹配了。
這時(shí)就需要用contains()函數(shù)了,代碼如下:
result = html.xpath('//li[contains(@class, "li")]/a/text()')這樣就會(huì)得到結(jié)果['first item']。
12.多屬性匹配
另外,我們還會(huì)碰到另一種情況,那就是依據(jù)多個(gè)屬性來確定一個(gè)節(jié)點(diǎn),這時(shí)就需要同時(shí)匹配多個(gè)屬性,此時(shí)我們可以使用運(yùn)算符and來連接。
from lxml import etreetext = ''' <li class="li li-first" name="item"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()') print(result)這里的li節(jié)點(diǎn)又增加了一個(gè)屬性name。要確定這個(gè)節(jié)點(diǎn),需要用時(shí)根據(jù)class和name來選擇,中間用and相連,相連之后置于中括號(hào)內(nèi)進(jìn)行條件篩選。除了and之外還有其他的運(yùn)算符可以使用。
13.順序選擇
我們?cè)谶x擇的時(shí)候某些屬性可能已經(jīng)匹配了多個(gè)節(jié)點(diǎn),但是我們只想要其中的某個(gè)節(jié)點(diǎn),如第二個(gè)節(jié)點(diǎn)或者最后一個(gè)節(jié)點(diǎn)。
from lxml import etreetext = ''' <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">third item</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> </ul> </div> ''' html = etree.HTML(text) result = html.xpath('//li[1]/a/text()') print(result) result = html.xpath('//li[last()]/a/text()') print(result) result = html.xpath('//li[position()<3]/a/text()') print(result) result = html.xpath('//li[last()-2]/a/text()') print(result)第一次選擇時(shí),我們選取了第一個(gè)li節(jié)點(diǎn),這里在中括號(hào)中傳入1就可。
第二次選擇時(shí),我們選取最后一個(gè)li節(jié)點(diǎn),中括號(hào)中傳入last()即可。
第三次選擇時(shí),我們選取了位置小于3的li節(jié)點(diǎn),運(yùn)用了position()函數(shù)。
第四次選擇時(shí),我們選擇了都輸?shù)谌齻€(gè)li節(jié)點(diǎn),中括號(hào)中傳入last()-2即可。
在Xpath中,提供了100多個(gè)函數(shù),包括存取、數(shù)值、字符串、邏輯、節(jié)點(diǎn)、序列等的處理功能。
14.節(jié)點(diǎn)軸選擇
XPath提供了許多節(jié)點(diǎn)軸選擇方法,包括獲取子元素、兄弟元素、父元素、祖先元素等,示例如下:
from lxml import etreetext = ''' <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">third item</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> </ul> </div> ''' html = etree.HTML(text) result = html.xpath('//li[1]/ancestor::*') print(result) result = html.xpath('//li[1]/ancestor::div') print(result) result = html.xpath('//li[1]/attribute::*') print(result) result = html.xpath('//li[1]/child::a[@href="link1.html"]') print(result) result = html.xpath('//li[1]/descendant::span') print(result) result = html.xpath('//li[1]/following::*[2]') print(result) result = html.xpath('//li[1]/following-sibling::*') print(result)第一次選擇時(shí),我們調(diào)用了ancestor軸,可以獲取所有的祖先節(jié)點(diǎn)。之后需要跟兩個(gè)冒號(hào),然后是節(jié)點(diǎn)的選擇器,這里我們使用*,表示匹配所有節(jié)點(diǎn)。
第二次選擇時(shí),我們又加了限定條件div,這時(shí)得到的結(jié)果就只有div這個(gè)祖先節(jié)點(diǎn)了。
第三次選擇時(shí),我們調(diào)用了attribute軸,可以獲取所有屬性值,其建甌跟的選擇器還是*,代表獲取節(jié)點(diǎn)的所有屬性。
第四次選擇時(shí),我們調(diào)用了child軸,可以獲取所有直接子節(jié)點(diǎn)。這里我們又加入了限定條件,選取href屬性為link1.html的a節(jié)點(diǎn)。
第五次選擇時(shí),我們調(diào)用了descendant軸,可以獲取所有的子孫節(jié)點(diǎn)。這里我們又加入了限制條件獲取span節(jié)點(diǎn)。所以返回的結(jié)果只包含span節(jié)點(diǎn)而不包括a節(jié)點(diǎn)。
第六次選擇時(shí),我們調(diào)用了following軸,可以獲取當(dāng)前節(jié)點(diǎn)之后的所有節(jié)點(diǎn)。這里我們使用*匹配,但又加入了索引選擇,所以只獲取了第二個(gè)后續(xù)節(jié)點(diǎn)。
第七次選擇時(shí),我們調(diào)用了following-sibling軸,可以獲取當(dāng)前節(jié)點(diǎn)之后的所有同級(jí)節(jié)點(diǎn)。這里我們使用*匹配,獲取所有后續(xù)同級(jí)節(jié)點(diǎn)。
參考書目:《Python 3 網(wǎng)絡(luò)爬蟲開發(fā)實(shí)戰(zhàn)》
總結(jié)
以上是生活随笔為你收集整理的xpath中两个冒号_爬虫学习(5)—XPath的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 好书推荐之【代码整洁之道】
- 下一篇: 一些常用的IOS开发网站