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編程

專欄目錄:目錄

爬蟲目錄:爬蟲系列目錄

版本說明:軟體及包版本說明


推薦閱讀:

TAG:Python入門 | python爬蟲 | 爬蟲計算機網路 |