xpath

XPath,全稱XML Path Language,即XML路徑語言,它是一門在XML文檔中查找信息的語言。它最初是用來搜尋XML文檔的,可是它一樣適用於HTML文檔的搜索。html

因此在作爬蟲時,咱們徹底可使用XPath來作相應的信息抽取。本節中,咱們就來介紹XPath的基本用法。node

1. XPath概覽

XPath的選擇功能十分強大,它提供了很是簡潔明瞭的路徑選擇表達式。另外,它還提供了超過100個內建函數,用於字符串、數值、時間的匹配以及節點、序列的處理等。幾乎全部咱們想要定位的節點,均可以用XPath來選擇。python

XPath於1999年11月16日成爲W3C標準,它被設計爲供XSLT、XPointer以及其餘XML解析軟件使用,更多的文檔能夠訪問其官方網站:https://www.w3.org/TR/xpath/git

2. XPath經常使用規則

表4-1列舉了XPath的幾個經常使用規則。github

表4-1 XPath經常使用規則網絡

表達式app

描述函數

nodename網站

選取此節點的全部子節點ui

/

從當前節點選取直接子節點

//

從當前節點選取子孫節點

.

選取當前節點

..

選取當前節點的父節點

@

選取屬性

這裏列出了XPath的經常使用匹配規則,示例以下:

 

 

這就是一個XPath規則,它表明選擇全部名稱爲title,同時屬性lang的值爲eng的節點。

後面會經過Python的lxml庫,利用XPath進行HTML的解析。

3. 準備工做

使用以前,首先要確保安裝好lxml庫,若沒有安裝,能夠參考第1章的安裝過程。

4. 實例引入

如今經過實例來感覺一下使用XPath來對網頁進行解析的過程,相關代碼以下:

 

 

這裏首先導入lxml庫的etree模塊,而後聲明瞭一段HTML文本,調用HTML類進行初始化,這樣就成功構造了一個XPath解析對象。這裏須要注意的是,HTML文本中的最後一個li節點是沒有閉合的,可是etree模塊能夠自動修正HTML文本。

這裏咱們調用tostring()方法便可輸出修正後的HTML代碼,可是結果是bytes類型。這裏利用decode()方法將其轉成str類型,結果以下:

 

 

能夠看到,通過處理以後,li節點標籤被補全,而且還自動添加了bodyhtml節點。

另外,也能夠直接讀取文本文件進行解析,示例以下:

 

 

其中test.html的內容就是上面例子中的HTML代碼,內容以下:

 

 

此次的輸出結果略有不一樣,多了一個DOCTYPE的聲明,不過對解析無任何影響,結果以下:

 

 

5. 全部節點

咱們通常會用//開頭的XPath規則來選取全部符合要求的節點。這裏之前面的HTML文本爲例,若是要選取全部節點,能夠這樣實現:

 

 

運行結果以下:

 

 

這裏使用*表明匹配全部節點,也就是整個HTML文本中的全部節點都會被獲取。能夠看到,返回形式是一個列表,每一個元素是Element類型,其後跟了節點的名稱,如htmlbodydivullia等,全部節點都包含在列表中了。

固然,此處匹配也能夠指定節點名稱。若是想獲取全部li節點,示例以下:

 

 

這裏要選取全部li節點,可使用//,而後直接加上節點名稱便可,調用時直接使用xpath()方法便可。

運行結果:

 

 

這裏能夠看到提取結果是一個列表形式,其中每一個元素都是一個 Element對象。若是要取出其中一個對象,能夠直接用中括號加索引,如[0]

6. 子節點

咱們經過///便可查找元素的子節點或子孫節點。假如如今想選擇li節點的全部直接a子節點,能夠這樣實現:

 

 

這裏經過追加/a即選擇了全部li節點的全部直接a子節點。由於//li用於選中全部li節點,/a用於選中li節點的全部直接子節點a,兩者組合在一塊兒即獲取全部li節點的全部直接a子節點。

運行結果以下:

 

 

此處的/用於選取直接子節點,若是要獲取全部子孫節點,就可使用//。例如,要獲取ul節點下的全部子孫a節點,能夠這樣實現:

 

 

運行結果是相同的。

可是若是這裏用//ul/a,就沒法獲取任何結果了。由於/用於獲取直接子節點,而在ul節點下沒有直接的a子節點,只有li節點,因此沒法獲取任何匹配結果,代碼以下:

 

 

運行結果以下:

 

 

所以,這裏咱們要注意///的區別,其中/用於獲取直接子節點,//用於獲取子孫節點。

7. 父節點

咱們知道經過連續的///能夠查找子節點或子孫節點,那麼假如咱們知道了子節點,怎樣來查找父節點呢?這能夠用..來實現。

好比,如今首先選中href屬性爲link4.htmla節點,而後再獲取其父節點,而後再獲取其class屬性,相關代碼以下:

 

 

運行結果以下:

 

 

檢查一下結果發現,這正是咱們獲取的目標li節點的class

同時,咱們也能夠經過parent::來獲取父節點,代碼以下:

 

 

8. 屬性匹配

在選取的時候,咱們還能夠用@符號進行屬性過濾。好比,這裏若是要選取classitem-1li節點,能夠這樣實現:

 

 

這裏咱們經過加入[@class="item-0"],限制了節點的class屬性爲item-0,而HTML文本中符合條件的li節點有兩個,因此結果應該返回兩個匹配到的元素。結果以下:

 

 

可見,匹配結果正是兩個,至因而不是那正確的兩個,後面再驗證。

9. 文本獲取

咱們用XPath中的text()方法獲取節點中的文本,接下來嘗試獲取前面li節點中的文本,相關代碼以下:

 

 

運行結果以下:

 

 

奇怪的是,咱們並無獲取到任何文本,只獲取到了一個換行符,這是爲何呢?由於XPath中text()前面是/,而此處/的含義是選取直接子節點,很明顯li的直接子節點都是a節點,文本都是在a節點內部的,因此這裏匹配到的結果就是被修正的li節點內部的換行符,由於自動修正的li節點的尾標籤換行了。

即選中的是這兩個節點:

 

 

其中一個節點由於自動修正,li節點的尾標籤添加的時候換行了,因此提取文本獲得的惟一結果就是li節點的尾標籤和a節點的尾標籤之間的換行符。

所以,若是想獲取li節點內部的文本,就有兩種方式,一種是先選取a節點再獲取文本,另外一種就是使用//。接下來,咱們來看下兩者的區別。

首先,選取到a節點再獲取文本,代碼以下:

 

 

運行結果以下:

 

 

能夠看到,這裏的返回值是兩個,內容都是屬性爲item-0li節點的文本,這也印證了前面屬性匹配的結果是正確的。

這裏咱們是逐層選取的,先選取了li節點,又利用/選取了其直接子節點a,而後再選取其文本,獲得的結果剛好是符合咱們預期的兩個結果。

再來看下用另外一種方式(即便用//)選取的結果,代碼以下:

 

 

運行結果以下:

 

 

不出所料,這裏的返回結果是3個。可想而知,這裏是選取全部子孫節點的文本,其中前兩個就是li的子節點a節點內部的文本,另一個就是最後一個li節點內部的文本,即換行符。

因此說,若是要想獲取子孫節點內部的全部文本,能夠直接用//text()的方式,這樣能夠保證獲取到最全面的文本信息,可是可能會夾雜一些換行符等特殊字符。若是想獲取某些特定子孫節點下的全部文本,能夠先選取到特定的子孫節點,而後再調用text()方法獲取其內部文本,這樣能夠保證獲取的結果是整潔的。

10. 屬性獲取

咱們知道用text()能夠獲取節點內部文本,那麼節點屬性該怎樣獲取呢?其實仍是用@符號就能夠。例如,咱們想獲取全部li節點下全部a節點的href屬性,代碼以下:

 

 

這裏咱們經過@href便可獲取節點的href屬性。注意,此處和屬性匹配的方法不一樣,屬性匹配是中括號加屬性名和值來限定某個屬性,如[@href="link1.html"],而此處的@href指的是獲取節點的某個屬性,兩者須要作好區分。

運行結果以下:

 

 

能夠看到,咱們成功獲取了全部li節點下a節點的href屬性,它們以列表形式返回。

11. 屬性多值匹配

有時候,某些節點的某個屬性可能有多個值,例如:

 

 

這裏HTML文本中li節點的class屬性有兩個值lili-first,此時若是還想用以前的屬性匹配獲取,就沒法匹配了,此時的運行結果以下:

 

 

這時就須要用contains()函數了,代碼能夠改寫以下:

 

 

這樣經過contains()方法,第一個參數傳入屬性名稱,第二個參數傳入屬性值,只要此屬性包含所傳入的屬性值,就能夠完成匹配了。

此時運行結果以下:

 

 

此種方式在某個節點的某個屬性有多個值時常常用到,如某個節點的class屬性一般有多個。

12. 多屬性匹配

另外,咱們可能還遇到一種狀況,那就是根據多個屬性肯定一個節點,這時就須要同時匹配多個屬性。此時可使用運算符and來鏈接,示例以下:

 

 

這裏的li節點又增長了一個屬性name。要肯定這個節點,須要同時根據classname屬性來選擇,一個條件是class屬性裏面包含li字符串,另外一個條件是name屬性爲item字符串,兩者須要同時知足,須要用and操做符相連,相連以後置於中括號內進行條件篩選。運行結果以下:

 

 

這裏的and實際上是XPath中的運算符。另外,還有不少運算符,如ormod等,在此總結爲表4-2。

表4-2 運算符及其介紹

運算符

描述

實例

返回值

or

age=19 or age=20

若是age是19,則返回true。若是age是21,則返回false

and

age>19 and age<21

若是age是20,則返回true。若是age18,則返回false

mod

計算除法的餘數

5 mod 2

1

|

計算兩個節點集

//book | //cd

返回全部擁有bookcd元素的節點集

+

加法

6 + 4

10

-

減法

6 - 4

2

*

乘法

6 * 4

24

div

除法

8 div 4

2

=

等於

age=19

若是age是19,則返回true。若是age是20,則返回false

!=

不等於

age!=19

若是age是18,則返回true。若是age是19,則返回false

<

小於

age<19

若是age是18,則返回true。若是age是19,則返回false

<=

小於或等於

age<=19

若是age是19,則返回true。若是age是20,則返回false

>

大於

age>19

若是age是20,則返回true。若是age是19,則返回false

>=

大於或等於

age>=19

若是age是19,則返回true。若是age是18,則返回false

此表參考來源:http://www.w3school.com.cn/xpath/xpath_operators.asp

13. 按序選擇

有時候,咱們在選擇的時候某些屬性可能同時匹配了多個節點,可是隻想要其中的某個節點,如第二個節點或者最後一個節點,這時該怎麼辦呢?

這時能夠利用中括號傳入索引的方法獲取特定次序的節點,示例以下:

 

 

第一次選擇時,咱們選取了第一個li節點,中括號中傳入數字1便可。注意,這裏和代碼中不一樣,序號是以1開頭的,不是以0開頭。

第二次選擇時,咱們選取了最後一個li節點,中括號中傳入last()便可,返回的即是最後一個li節點。

第三次選擇時,咱們選取了位置小於3的li節點,也就是位置序號爲1和2的節點,獲得的結果就是前兩個li節點。

第四次選擇時,咱們選取了倒數第三個li節點,中括號中傳入last()-2便可。由於last()是最後一個,因此last()-2就是倒數第三個。

運行結果以下:

 

 

這裏咱們使用了last()position()等函數。在XPath中,提供了100多個函數,包括存取、數值、字符串、邏輯、節點、序列等處理功能,它們的具體做用能夠參考:http://www.w3school.com.cn/xpath/xpath_functions.asp

14. 節點軸選擇

XPath提供了不少節點軸選擇方法,包括獲取子元素、兄弟元素、父元素、祖先元素等,示例以下:

 

 

運行結果以下:

 

 

第一次選擇時,咱們調用了ancestor軸,能夠獲取全部祖先節點。其後須要跟兩個冒號,而後是節點的選擇器,這裏咱們直接使用*,表示匹配全部節點,所以返回結果是第一個li節點的全部祖先節點,包括htmlbodydivul

第二次選擇時,咱們又加了限定條件,此次在冒號後面加了div,這樣獲得的結果就只有div這個祖先節點了。

第三次選擇時,咱們調用了attribute軸,能夠獲取全部屬性值,其後跟的選擇器仍是*,這表明獲取節點的全部屬性,返回值就是li節點的全部屬性值。

第四次選擇時,咱們調用了child軸,能夠獲取全部直接子節點。這裏咱們又加了限定條件,選取href屬性爲link1.htmla節點。

第五次選擇時,咱們調用了descendant軸,能夠獲取全部子孫節點。這裏咱們又加了限定條件獲取span節點,因此返回的結果只包含span節點而不包含a節點。

第六次選擇時,咱們調用了following軸,能夠獲取當前節點以後的全部節點。這裏咱們雖然使用的是*匹配,但又加了索引選擇,因此只獲取了第二個後續節點。

第七次選擇時,咱們調用了following-sibling軸,能夠獲取當前節點以後的全部同級節點。這裏咱們使用*匹配,因此獲取了全部後續同級節點。

以上是XPath軸的簡單用法,更多軸的用法能夠參考:http://www.w3school.com.cn/xpath/xpath_axes.asp

15. 結語

到如今爲止,咱們基本上把可能用到的XPath選擇器介紹完了。XPath功能很是強大,內置函數很是多,熟練使用以後,能夠大大提高HTML信息的提取效率。

若是想查詢更多XPath的用法,能夠查看:http://www.w3school.com.cn/xpath/index.asp

若是想查詢更多Python lxml庫的用法,能夠查看http://lxml.de/

轉載請註明:靜覓 » [Python3網絡爬蟲開發實戰] 4.1-使用XPath

相關文章
相關標籤/搜索