Python教程:Xpath 進階

人生苦短,我用 Python

引言

文接上篇,我們接着聊,上篇我們介紹了 Xpath 一些常用的匹配方式, DOM 節點我們可以匹配出來了,這並不是我們的最終目的,我們是要從這些節點中取出來我們想要的數據。本篇我們接着介紹如何使用 Xpath 獲取數據。

文本獲取

我們先嚐試下獲取第一篇文章的題目,獲取節點中的文本我們可以使用 text() 來進行獲取,如圖:

image

代碼如下:

from lxml import etree
import requests

response = requests.get('https://www.geekdigging.com/')
html_str = response.content.decode('UTF-8')
html = etree.HTML(html_str)

result_1 = html.xpath('/html/body/section/div/div/main/article[1]/div[2]/div/h3/a/text()')
print(result_1)

結果如下:

['小白學 Python 爬蟲(18):Requests 進階操作']

哇,上面示例裏面的表達式好長啊,這個怎麼寫出來的,怎麼寫的稍後再說,先介紹一下這個表達式的意思,仔細看一下,這個表達式其實是從整個 HTML 源代碼的最外層的 <html> 標籤寫起,一層一層的定位到了我們所需要的節點,然後再使用 text() 方法獲取了其中的文本內容。

關於這個表達式怎麼來的,肯定不是小編寫的,這麼寫講實話是有點傻,完全沒必要從整個文檔的最外層開始寫。

其實這個是 Chrome 幫我們生成的,具體操作可見下圖:

image

這時 Chrome 會自動幫我們把這個節點的表達式 copy 到當前的剪切板上,只需要我們在程序裏 ctrl + v 一下。

屬性獲取

有些情況下,我們可能不止需要節點中的文本數據,可能還會需要節點中的屬性數據,比如上面的示例,我們除了想知道文章標題,其實還想知道文章的跳轉路徑:

result_2 = html.xpath('/html/body/section/div/div/main/article[1]/div[2]/div/h3/a/@href')
print(result_2)

結果如下:

['/2019/12/11/1468953802/']

這裏需要注意的是,此處和屬性匹配的方法不同,屬性匹配是中括號加屬性名和值來限定某個屬性,如 [@class="container"] ,而此處的 @href 指的是獲取節點的某個屬性,二者需要做好區分。

屬性多值匹配

某些時候吧,某些節點的某個屬性可能有多個值,這個多見於 class 屬性,由於某些編碼習慣以及某些其他原因,這個屬性經常性會出現多個值,這時如果只使用其中的一個值的話,就無法匹配了。

image

如果這麼寫的話:

result_3 = html.xpath('//div[@class="post-head"]')
print(result_3)

結果如下:

[]

可以看到,這裏沒有匹配到任何節點,這時,我們可以使用一個函數:contains() ,上面的示例可以改成這樣:

result_3 = html.xpath('//div[contains(@class, "post-head")]')
print(result_3)

這樣通過 contains() 方法,第一個參數傳入屬性名稱,第二個參數傳入屬性值,只要此屬性包含所傳入的屬性值,就可以進行匹配了。

多屬性匹配

除了上面的一個屬性有多個值的情況,還經常會出現需要使用多個屬性才能確定一個唯一的節點。

這時,我們可以使用運算符來進行處理。

image

還是這個示例,我們獲取 <img> 這個節點,如果只是使用 class 屬性來進行獲取,會獲得很多個節點:

result_4 = html.xpath('//img[@class="img-ajax"]')
print(result_4)

結果如下:

[<Element img at 0x1984505a788>, <Element img at 0x1984505a7c8>, <Element img at 0x1984505a808>, <Element img at 0x1984505a848>, <Element img at 0x1984505a888>, <Element img at 0x1984505a908>, <Element img at 0x1984505a948>, <Element img at 0x1984505a988>, <Element img at 0x1984505a9c8>, <Element img at 0x1984505a8c8>, <Element img at 0x1984505aa08>, <Element img at 0x1984505aa48>]

如果我們加上 alt 屬性一起進行匹配的話,就可以獲得唯一的節點:

result_4 = html.xpath('//img[@class="img-ajax" and @alt="小白學 Python 爬蟲(18):Requests 進階操作"]')
print(result_4)

結果如下:

[<Element img at 0x299905e6a48>]

Xpath 支持很多的運算符,詳細見下表(來源:https://www.w3school.com.cn/xpath/xpath_operators.asp)

運算符 描述 實例 返回值
計算兩個節點集 //book
+ 加法 6 + 4 10
- 減法 6 - 4 2
* 乘法 6 * 4 24
div 除法 8 div 4 2
= 等於 price=9.80 如果 price 是 9.80,則返回 true。如果 price 是 9.90,則返回 false。
!= 不等於 price!=9.80 如果 price 是 9.90,則返回 true。如果 price 是 9.80,則返回 false。
< 小於 price<9.80 如果 price 是 9.00,則返回 true。如果 price 是 9.90,則返回 false。
<= 小於或等於 price<=9.80 如果 price 是 9.00,則返回 true。如果 price 是 9.90,則返回 false。
> 大於 price>9.80 如果 price 是 9.90,則返回 true。如果 price 是 9.80,則返回 false。
>= 大於或等於 price>=9.80 如果 price 是 9.90,則返回 true。如果 price 是 9.70,則返回 false。
or price=9.80 or price=9.70 如果 price 是 9.80,則返回 true。如果 price 是 9.50,則返回 false。
and price>9.00 and price<9.90 如果 price 是 9.80,則返回 true。如果 price 是 8.50,則返回 false。
mod 計算除法的餘數 5 mod 2 1

按順序選擇

有些時候,我們匹配出來很多的節點,但是我們只想獲取其中的某一個節點,比如第一個或者最後一個,這時可以使用中括號傳入索引的方法獲取特定次序的節點。

我們還是文章的題目爲例,我們先獲取所有的文章題目,再進行選擇,示例代碼如下:

result_5 = html.xpath('//article/div/div/h3[@class="post-title"]/a/text()')
print(result_5)
result_6 = html.xpath('//article[1]/div/div/h3[@class="post-title"]/a/text()')
print(result_6)
result_7 = html.xpath('//article[last()]/div/div/h3[@class="post-title"]/a/text()')
print(result_7)
result_8 = html.xpath('//article[position() < 5]/div/div/h3[@class="post-title"]/a/text()')
print(result_8)

結果如下:

['小白學 Python 爬蟲(18):Requests 進階操作', '小白學 Python 爬蟲(17):Requests 基礎使用', '小白學 Python 爬蟲(16):urllib 實戰之爬取妹子圖', '如何用 Python 寫一個簡易的抽獎程序', '小白學 Python 爬蟲(15):urllib 基礎使用(五)', '我們真的在被 APP “竊聽” 麼?', '小白學 Python 爬蟲(14):urllib 基礎使用(四)', '小白學 Python 爬蟲(13):urllib 基礎使用(三)', '小白學 Python 爬蟲(12):urllib 基礎使用(二)', '小白學 Python 爬蟲(11):urllib 基礎使用(一)', '老司機大型車禍現場', '小白學 Python 爬蟲(10):Session 和 Cookies']
['小白學 Python 爬蟲(18):Requests 進階操作']
['小白學 Python 爬蟲(10):Session 和 Cookies']
['小白學 Python 爬蟲(18):Requests 進階操作', '小白學 Python 爬蟲(17):Requests 基礎使用', '小白學 Python 爬蟲(16):urllib 實戰之爬取妹子圖', '如何用 Python 寫一個簡易的抽獎程序']

第一次,我們選取了當前頁面所有的文章的題目。

第二次,我們選擇了當前頁面第一篇文章的題目,這裏注意下,中括號中傳入數字1即可,這裏的開始是以 1 爲第一個的,不是程序中的 0 爲第一個。

第三次,我們使用 last() 函數,獲取了最後一篇文章的題目。

第四次,我們選擇了位置小於 5 的文章題目。

節點軸

軸可定義相對於當前節點的節點集。

軸名稱 結果
ancestor 選取當前節點的所有先輩(父、祖父等)。
ancestor-or-self 選取當前節點的所有先輩(父、祖父等)以及當前節點本身。
attribute 選取當前節點的所有屬性。
child 選取當前節點的所有子元素。
descendant 選取當前節點的所有後代元素(子、孫等)。
descendant-or-self 選取當前節點的所有後代元素(子、孫等)以及當前節點本身。
following 選取文檔中當前節點的結束標籤之後的所有節點。
namespace 選取當前節點的所有命名空間節點。
parent 選取當前節點的父節點。
preceding 選取文檔中當前節點的開始標籤之前的所有節點。
preceding-sibling 選取當前節點之前的所有同級節點。
self 選取當前節點。

我們以 ancestor 軸來做示例:

# 節點軸示例
# 獲取所有祖先節點
result_9 = html.xpath('//article/ancestor::*')
print(result_9)
# 獲取祖先節點 main 節點
result_10 = html.xpath('//article/ancestor::main')
print(result_10)

結果如下:

[<Element html at 0x2a266171208>, <Element body at 0x2a266171248>, <Element section at 0x2a266171288>, <Element div at 0x2a2661712c8>, <Element div at 0x2a266171308>, <Element main at 0x2a266171388>]
[<Element main at 0x2a266171388>]

關於節點軸就先介紹到這裏

對Python感興趣或者是正在學習的小夥伴,可以加入我們的Python學習扣qun:855408893 ,從0基礎的python腳本到web開發、爬蟲、django、數據挖掘數據分析等,0基礎到項目實戰的資料都有整理。送給每一位python的小夥伴!每晚分享一些學習的方法和需要注意的小細節,學習路線規劃,利用編程賺外快。點擊加入我們的 python學習圈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章