Xpath:簡單易用的網頁內容提取工具
學習一時爽,一直學習一直爽 !
Hello,大家好,我是Connor,一個從無到有的技術小白。上一次我們說到了 requests
的使用方法。到上節課為止,我們已經學完了所有的 Python 常用的訪問庫。那麼當我們獲取到了訪問的內容之後,我們就應該從網頁上提取我們想要的內容了。所以,今天我們來講網頁內容的常用提取工具之一:Xpath
。相比於 BeautifulSoup
而言,Xpath
更加簡單易上手。
1.Xpath簡介
Xpath
是一門在 XML 文件中查詢資訊的語言。XPath 用於在 XML 文件中通過元素和屬性進行導航。他是一種路徑語言(XML Path Language),用來確定XML文件中某部分的位置。
XPath基於XML的樹狀結構,提供在資料結構樹中找尋節點的能力。起初XPath的提出的初衷是將其作為一個通用的、介於XPointer與XSL間的語法模型。但是XPath很快的被開發者採用來當作小型*查詢語言被廣泛使用。比如說,當你開啟一個網頁後按 F12
進行元素檢查。當你想要複製某個元素的路徑的時候,你可以通過右鍵進行 Copy 操作。你會發現裡面有 Copy Xpath
的選項。由此可見 Xpath 使用的廣泛程度。
說了這麼多Xpath使用的怎麼怎麼廣泛,怎麼怎麼好用,我們還是來點實在的,看看在 Python 爬蟲中到底如何使用 Xpath
來抓取我們想要的內容吧:
2. Xpath的安裝
在前面的教程中,我幾乎從未提過某個庫的安裝,但是為什麼在這裡我要提出如何安裝呢?原因很簡單,Xpath只是 lxml
庫中的一個模組,在 Python 很多庫中都提供有 Xpath
的功能,但是最基本的還是 lxml
的這個庫。效率最高。所以,你知道了,想要使用 Xpath
那麼你就需要安裝 lxml
庫:
pip install lxml
複製程式碼
3.Xpath的語法
其實說白了,Xpath
就是從 html 中選取節點。節點是通過沿著路徑或者通過 step 來選取的。下面,我們將通過如下HTML文件來進行演示:
html_doc ="""
<html>
<head></head>
<body>
<li>
<a href="/book_16860.html" title="總裁的新鮮小妻">
<img src="/16860s.jpg">
</a>
<img src="/kukuku/images/only.png" class="topss png_bg">
<img src="abc.png" class="topss png_bg">
<a href="/book_16860.html">總裁的新鮮小妻子</a>
</li>
<li>
<a href="/book_16861.html" title="鬥神天下">
<img src="/16861s.jpg">
</a>
<img src="/kukuku/images/only.png" class="topss png_bg">
<img src="def.png" class="topss png_bg">
<a href="/book_16861.html">鬥神天下</a>
</li>
</body>
</html>"""
複製程式碼
首先,大家也都知道我們實際上從網頁上獲取的都是字串格式。那麼如果我們想要通過 Xpath 來提取我們想要的內容,我們首先要生成 HTML 的 DOM 樹:
from lxml import etree
page = etree.HTML(html_doc)
複製程式碼
3.1 路徑查詢
如果我們想要使用路徑查詢,那我們首先需要知道 Xpath 的語法。Xpath 的主要語法有如下:
表示式 | 描述 |
---|---|
nodename | 選取名為nodename的子節點 |
/ | 從根節點選取 |
// | 從匹配選擇的當前節點選擇文件中的節點,而不考慮它們的位置。 |
. | 選取當前節點 |
.. | 選取當前節點的父節點 |
@ | 選取屬性 |
- 查詢當前的子節點:
In [1]: page.xpath('head')
Out[1]: [<Element head at 0x7f185bfe5b08>]
複製程式碼
當前的節點位置是在 html,所以直接查詢 head
節點可以查詢出來,li
是html的孫節點,如果查詢 li
將返回空值:
In [2]: page.xpath('li')
Out[2]: []
複製程式碼
- 從根節點進行查詢:
In [3]: page.xpath('/html')
Out[3]: [<Element html at 0x11208be88>]
複製程式碼
從根節點進行查詢,根節點下只有一個節點 html
節點,所以從根節點查詢只能查詢到 html
,如果查詢其它內容將返回空列表:
In [4]: page.xpath('/li')
Out[4]: []
複製程式碼
- 從整個文件所有節點查詢:
In [5]: page.xpath('//li')
Out[5]:
[<Element li at 0x1128c02c8>,
<Element li at 0x111c74108>,
<Element li at 0x111fd2288>,
<Element li at 0x1128da348>]
複製程式碼
從整個文件進行查詢可以查詢整個文件中符合條件的節點,包括孫節點及以下。
- 選取當前節點的父節點
In [6]: page.xpath('//li')[0].xpath('..')
Out[6]: [<Element body at 0x1128c0ac8>]
複製程式碼
- 選取屬性:
In [7]: page.xpath('//a')[1].xpath('@href')
Out[7]: ['/book_16860.html']
複製程式碼
選取屬性支援任意的標籤屬性,但是要注意如果選取出的節點有多個,需要指定是哪一個節點的屬性。
3.2 節點查詢
通過路徑查詢到節點以後,就需要從超找到的節點中選取我們需要的內容了。查詢節點也有一些語法,如下:
表示式 | 結果 |
---|---|
nodename[index] | 選取符合要求的第 index 個元素 |
nodename[last()] | 選取最後一個元素 |
nodename[position()< num] | 選取前 num 個元素 |
nodename[@attribute] | 選取帶有屬性名為 attribute 的元素 |
nodename[@attribute='value'] | 選取帶有屬性名為 attribute 且 值為 value 的元素 |
- 選取第一個 img 節點的 src 屬性:
In [1]: page.xpath('//li[1]/a[1]/img[1]/@src')
Out[1]: ['/16860s.jpg']
複製程式碼
**注意:當你在選取一個節點的屬性的時候,有一點需要注意。通過[index]選取出的節點是每一個符合條件的第[index]個符合條件的元素。**但是這麼說比較抽象,我們舉個例子。例如:
In [2]: page.xpath('//li[1]') 選取所有符合 li 節點的第一個節點
Out[2]: [<Element li at 0x7fb517327ac8>]
複製程式碼
通過上面的例子我們貌似看不出什麼,那我們再看下面的這個例子:
In [3]: page.xpath('//li//img[1]')
Out[3]: [<Element img at 0x7f0c26328b08>,
<Element img at 0x7f0c26328a88>,
<Element img at 0x7f0c26328bc8>,
<Element img at 0x7f0c26328c08>]
複製程式碼
你可以看到,我們選取出了4個 img 節點。原因很簡單,看下面的程式碼和解釋就能明白了:
<html>
<head></head>
<body>
<li>
<a href="/book_16860.html" title="總裁的新鮮小妻">
(一)<img src="/16860s.jpg">
</a>
(二)<img src="/kukuku/images/only.png" class="topss png_bg">
(三)<img src="abc.png" class="topss png_bg">
<a href="/book_16860.html">總裁的新鮮小妻子</a>
</li>
<li>
<a href="/book_16861.html" title="鬥神天下">
(四)<img src="/16861s.jpg">
</a>
(五)<img src="/kukuku/images/only.png" class="topss png_bg">
(六)<img src="def.png" class="topss png_bg">
<a href="/book_16861.html">鬥神天下</a>
</li>
</body>
</html>
複製程式碼
- (一)是第一個
<li>
節點的<a>
節點裡的第一個<img>
節點。其路徑為<li>/<a>/<img>
- (二)是第一個
<li>
節點的<img>
節點裡的第一個<img>
節點。其路徑為<li>/<img>
。 - (三)之所以沒有被選中,是因為它是
<li>
標籤下的第二個<img>
標籤 - (四)(五)(六)同理
- 選取第二個元素開始的所有節點:
In [4]: page.xpath('//img[position()>1]')
Out[4]: [<Element img at 0x7f78ba63dac8>, <Element img at 0x7f78ba63da48>]
複製程式碼
- 選取帶有指定屬性與指定值的節點:
In [5]: page.xpath('//a[@title="鬥神天下"]')
Out[5]: [<Element a at 0x7fdd0844fa48>]
複製程式碼
3.3 未知節點的匹配
當我們匹配時會出現路徑不確定的情況,這個時候我們就要涉及到匹配未知節點。匹配未知節點也有對應的語法,如下:
萬用字元 | 描述 |
---|---|
* | 匹配任何元素節點 |
@* | 匹配任何屬性節點 |
匹配任何屬性節點:
In [1]: page.xpath('//li/a/*')
Out[1]: [<Element img at 0x7f83af768b08>,
<Element img at 0x7f83af768a88>,
<Element img at 0x7f83af768bc8>,
<Element img at 0x7f83af768c08>]
複製程式碼
匹配任何元素節點:
In [2]: page.xpath('//li/a[@*]')
Out[2]: [<Element a at 0x7ff2dcf69b08>,
<Element a at 0x7ff2dcf69a88>,
<Element a at 0x7ff2dcf69bc8>,
<Element a at 0x7ff2dcf69c08>]
複製程式碼
3.4 獲取節點中的文字
通過 屬性方法可以獲取屬性內的內容,但是位於節點之間的內容無法獲取到,這個時候就可以通過 text()
與 string()
方法來獲得其中的文字:
通過 text()
獲取某個節點中的文字:
In [1]: page.xpath('//li/a[3]/text()')
Out[1]: ['總裁的新鮮小妻子', '鬥神天下']
複製程式碼
可以看到,通過 text()
屬性可以很輕鬆的獲取標籤之間的文字。
通過 string()
獲取某個節點中的文字:
In [1]: page.xpath('string(//li[1]/a[3])')
Out[1]: '總裁的新鮮小妻子'
複製程式碼
3.5 選取多個路徑
有的時候我們需要同時查詢多個條件,這個時候你可以通過在路徑表示式中使用管道符("|"),選取若干個路徑:
In [1]: page.xpath('//li[1]/img[2]/@src | //li[1]/a[3]/text()')
Out[1]: ['/kukuku/images/second.png', '總裁的新鮮小妻子']
複製程式碼
同時選取多個路徑並不會生成一個新的列表,只有一個列表,所以你要考慮好如何提取你的返回結果。一般情況下還是不建議使用多條件來進行查詢,因為多種提取結果混在一個列表不利於提取,即便可以提取,但也會增加計算量,降低程式效能。所以,儘量不要使用這種方法。
下期預告:
Xpath用來用去還是那麼費勁啊,有沒有什麼別的更簡單的方法啊???當然有的,你可以喝著美味的湯就做完你需要做的事情了。敬請期待下期——BeautifulSoup:美味的湯
以上就是所有的 Xpath 有關的內容啦,可能並不比別人講的細,但是已經足夠你做爬蟲使用了。其實我也希望能夠講的更加細緻一點,奈何我是一個正則黨,我個人不太喜歡使用Xpath,如果你真的想要深入瞭解Xpth的話,推薦你到 W3C Xpath教程 >>> 去看看它們是怎麼用的。
好了,這就是今天的內容了,我是Connor,一個從無到有的技術小白。不知道你今天又收穫了多少呢???我們下期再見!
學習一時爽,一直學習一直爽 !
系列文章連線:
Python 爬蟲十六式 - 第一式:HTTP協議 >>>
Python 爬蟲十六式 - 第二式:urllib 與 urllib3 >>>
Python 爬蟲十六式 - 第三式:Requests的用法 >>>
Python 爬蟲十六式 - 第五式:BeautifulSoup,美味的湯 >>>
Python 爬蟲十六式 - 第六式:JQuery的假兄弟-pyquery >>>
Python 爬蟲十六式 - 第七式:正則的藝術 >>>
Python 爬蟲十六式 - 第八式:例項解析-全書網 >>>