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