xpath全面總結
xpath語法是解析網頁最常用的方法之一,本文講解一下如何在lxml庫中使用xpath語法進行網頁的解析。
本文分為如下幾個部分
- 解析html流程說明
- 提取內容
- 識別標籤
解析html流程說明
我們先看一個例子了解使用lxml的流程
在下面的html代碼中其提取如下部分
- 標題
- 文字1
- 文字1和文字2
- 列表1第1項 列表1第2項
a = <title>標題</title><body> <ul class=list1> <li>列表1第1項</li> <li>列表1第2項</li> </ul> <p class=first>文字1</p> <p class=second>文字2</p> <ul class=list2> <li>列表2第1項</li> <li>列表2第2項</li> </ul></body>from lxml import etreehtml = etree.HTML(a)html.xpath(//title/text())[0] # 標題html.xpath("//p[@class=first]//text()")[0] # 文字1html.xpath("//p/text()") # [文字1, 文字2]html.xpath("//ul[@class=list1]/li/text()") # [列表1第1項, 列表1第2項]
可以先總結一下xpath語法的一些規律
- 用xpath語法提取基於節點路徑,每次只要輸入一個字元串,字元串按照格式填寫就能完成提取
- 格式是一層層的節點,用
/
分離,通過[]
在後面加入該節點的屬性判斷來唯一確定節點位置 - 提取的結果永遠是list,最後都要用索引提取
- 不特殊指定的情況下會自動尋找所有滿足條件的結果,可以在提取結果中用索引篩選,也可以在xpath內部使用索引
- 語法中分隔節點有兩種方法
/
和//
,它們之間的差別在於,前者只尋找子節點,後者會尋找子孫所有後代的節點,將所有滿足條件的全都找到 - 剛開始總是用
//
是因為我們不會從第一個節點一個一個寫,寫到我們想要的節點上去,這樣代碼就太長了。所以直接從中間找到一個便於唯一確定我們要找的位置的節點,再往後找就行了 - xpath好像不能查看一個節點所有屬性以及專門去判斷有沒有某屬性等
接下來我們來講提取細節,首先載入庫
from lxml import etree
提取內容
因為xpath提取到的整個標籤展現形式是這樣的[<Element h1 at 0x2122e994748>]
,所以先講如何提取內容,之後講如何提取標籤時,才好根據輸出內容來區分提出來的是什麼東西。
提取內容分為兩個部分
- 提取標籤內容,用
/text()
- 提取標籤屬性值,用
/@屬性名
見如下代碼
a = <body> <h><a href=www.biaoti.com>head</a></h> <p>段落1</p> <p>段落2</p></body>html = etree.HTML(a)html.xpath(//h) # [<Element h at 0x2122e64e4c8>]# 用//text() 提取標籤內容html.xpath(//h//text()) # [head]# /text()和//text()的區別在於不可以提取孫節點的內容html.xpath(//h/a/text()) # [head]# xpath語法默認提取全部html.xpath(//p/text()) # [段落1, 段落2]html.xpath(//p[1]/text()) # [段落1]# html.xpath(//body//text()) # [
, head,
, 段落1,
, 段落2,
]# 提取標籤屬性html.xpath(//h/a/@href) # [www.biaoti.com]
識別標籤
只根據標籤來識別
xpath語法對正則表達式支持比較弱,正則的使用主要體現在屬性的篩選上,所以這裡就不使用正則了
a = <body> <h1>head</h1> <h2>標題2</h2> <h2>標題3</h2></body>html = etree.HTML(a)# 只提取標籤時地址形式,用tostring轉化為字元串形式,但是對中文轉化會亂碼html.xpath(//h1) # [<Element h1 at 0x2122e6d8dc8>]etree.tostring(html.xpath(//h1)[0]) # b<h1>head</h1>
# 簡單提取標籤html.xpath(//h1/text()) # 一個列表 [head]html.xpath(//h1/text())[0] # 提取出成字元串 head# 使用「或」運算符html.xpath((//h1|//h2)/text())
同時根據標籤和屬性識別
a = <p id=p1>段落1</p><p id=p2>段落2</p><p class=p3>段落3</p><p class=p3 id=pp>段落4</p>html = etree.HTML(a)html.xpath(//p[@id="p1"]/text()) # [段落1]html.xpath(//p[@class]/text()) # 含有這個屬性 [段落3, 段落4]html.xpath(//p[@class="p3" and @id="pp"]/text()) # 使用多個屬性同時滿足 [段落4]html.xpath(//p[@class or @id="p1"]/text()) # 多個屬性滿足一個即可 [段落1, 段落3, 段落4]html.xpath(//p[@id="p1" or @id="p2"]/text()) # [段落1, 段落2]html.xpath(//p[not(@id) and @class="p3"]/text()) # 不含有id屬性 [段落3]html.xpath(//*[@id="p1"]/text()) # 不需要任意標籤名皆可 [段落1]# 類似正則表達式html.xpath(//p[starts-with(@id,"p")]/text()) # 匹配開頭 [段落1, 段落2, 段落4]html.xpath(//p[contains(@id,"1")]/text()) # 包含即可 [段落1]# 正則表達式html.xpath(r//p[re:match(@id, "^p")]/text(), namespaces={"re": "http://exslt.org/regular-expressions"}) # 這裡的match是包含的意思
根據標籤內內容來識別
根據內容來識別和根據屬性非常相似
a = <p id=p1>段落1</p><p class=p3>段落2</p><p class=p3>文章</p><p></p>html = etree.HTML(a)html.xpath(//p[text()="文章"]/@class) # [p3]html.xpath(//p[text()="段落1" or text()="段落2"]/@*) # 提取任意屬性 [p1, p3]html.xpath(//p[starts-with(text(),"段落")]/@*) # [p1, p3]html.xpath(r//p[re:match(text(), "^段落")]/@*, namespaces={"re": "http://exslt.org/regular-expressions"}) # [p1, p3]
根據位置識別
相比於Beautifulsoup,xpath對位置的把握更加靈活
a = <title>標題</title><body> <ul class=list1> <li>列表1第1項</li> <li>列表1第2項</li> </ul> <p class=first>文字1</p> <p class=second>文字2</p> <ul class=list2> <li>列表2第1項</li> <li>列表2第2項</li> </ul></body>html = etree.HTML(a)html.xpath(//ul[2]/li/text()) # 在xpath語句中使用索引,限制只在第二個ul中尋找html.xpath(//ul/li[1]/text()) # 在每個ul中取第一個li# [列表2第1項, 列表2第2項]html.xpath(//ul[last()]/li/text()) # 取最後一個html.xpath(//ul[last()-1]/li/text()) # 倒數第二個html.xpath(//ul[position()<3]/li/text()) # 前兩個
另外,如果想看xpath語句的列表,可以參考如下資料
- 崔慶才的博客
- w3school的教程
因為網頁結構繁多,不可能總結一篇覆蓋所有可能情況,所以實際使用時如果無法找到匹配的方法,可以藉助搜索引擎。
使用xpath進行真實網頁的爬蟲練習可以看這篇文章xpath+mongodb抓取伯樂在線實戰
專欄信息
專欄主頁:python編程
專欄目錄:目錄
爬蟲目錄:爬蟲系列目錄
版本說明:軟體及包版本說明
推薦閱讀: