前面的文章,我們已經學會正則表達式以及 BeautifulSoup庫的用法。我們領教了正則表達式的便捷,感受 beautifulSoup 的高效。本文介紹也是內容提取的工具 —— Xpath,它一般和 lxml 庫搭配使用。所以,我稱這兩者為“干將莫邪”。
1 Xpath 和 lxml
- Xpath
XPath即為XML路徑語言,它是一種用來確定XML(標準通用標記語言的子集)文檔中某部分位置的語言。XPath 基于 XML 的樹狀結構,提供在數據結構樹中找尋節點的能力。
Xpath 原本是用于選取 XML 文檔節點信息。XPath 是于 1999 年 11 月 16 日 成為 W3C 標準。因其既簡單方便又容易,所以它逐漸被人說熟知。
- lxml
lxml 是功能豐富又簡單易用的,專門處理 XML 和 HTML 的 Python 官網標準庫。
2 Xpath 的語法
正則表達式的枯燥無味又學習成本高,Xpath 可以說是不及其萬分之一。所以只要花上 10 分鐘,掌握 Xpath 不在話下。Xpath 的語言以及如何從 HTML dom 樹中提取信息,我將其歸納為“主干 - 樹支 - 綠葉”。
2.1 “主干” —— 選取節點
抓取信息,我們需知道要從哪里開始抓取。因此,需要找個起始節點。Xpath 選擇起始節點有以下可選:
表達式 | 描述 |
---|---|
nodename | 選取標簽節點的所有子節點。 |
/ | 從根節點選取,html DOM 樹的節點就是 html。 |
// | 從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置。 |
. | 選擇當前節點,一般用于二級提取。 |
.. | 選取當前節點的父節點,在二級提取時用到。 |
@ | 選取屬性。 |
我們通過以下實例來了解其用法:
路徑表達式 | 描述 |
---|---|
xpath('//div') | 選取 div 元素的所有子節點。 |
xpath('/div') | 選取 div 元素作為根節點。如果同級有多個 div ,那么所有 div 都會被選為根節點。 |
xpath('/div/span') | 選取屬于 div 元素<font color='red'>下</font>所有 span 元素節點。如果 span 有多個,也會被選中。 |
xpath('//div') | 選取所有 div 元素節點,不管它們在文檔的位置。 |
xpath('//div/span') | 選取 div 元素<font color='red'>下</font>的所有 span 元素節點,不管位于 div 之下的什么位置 |
xpath("http://@[class='content']") | 選取包含屬性 class 的值為 content 的節點,不管是 div 元素還是其他元素 |
xpath("http://@[id='center']") | 選取屬性 id 的值為 center 的節點,不管是 div 元素還是其他元素 |
如果你對于提取節點沒有頭緒的時候,可以使用通配符來暫時替代。等查看輸出內容之后再進一步確認。
路徑表達式 | 描述 |
---|---|
xpath('/div/*') | 選取 div 元素節點<font color='red'>下</font>的所有節點 |
xpath('/div[@*]') | 選取所有帶屬性的 div 元素節點 |
2.2 “分支” —— 關系節點與謂語
這一步的過程其實是通過起點一步步來尋找最終包含我們所需內容的節點。我們有時需要使用到相鄰節點信息。因此,我們需要了解關系節點或者謂語。
- 關系節點
一般而言,DOM 樹中一個普通節點具有父節點、兄弟節點、子節點。當然也有例外的情況。這些有些節點比較特殊,可能沒有父節點,如根節點;也有可能是沒有子節點,如深度最大的節點。Xpath 也是有支持獲取關系節點的語法。
關系 | 路徑表達式 | 描述 |
---|---|---|
parent(父) | xpath('./parent::*') | 選取當前節點的父節點 |
ancestor(先輩) | xpath('./ancestor::*') | 選取當前節點的所有先輩節點,包括父、祖父等 |
ancestor-or-self(先輩及本身) | xpath('./ancestor-or-self::*') | 選取當前節點的所有先輩節點以及節點本身 |
child(子) | xpath('./child::*') | 選取當前節點的所有子節點 |
descendant(后代) | xpath('./descendant::*') | 選取當前節點的所有后代節點,包括子節點、孫節點等 |
following | xpath('./following ::*') | 選取當前節點結束標簽后的所有節點 |
following-sibing | xpath('./following-sibing::*') | 選取當前節點之后的兄弟節點 |
preceding | xpath('./preceding::*') | 選取當前節點開始標簽前的所有節點 |
preceding-sibling | xpath('./parent::*') | 選取當前節點之前的兄弟節點 |
self(本身) | xpath('./self::*') | 選取當前節點本身 |
- 謂語
謂語用來查找某個特定的節點或者包含某個指定的值的節點。同時,它是被嵌在方括號中的。
路徑表達式 | 描述 |
---|---|
xpath('./body/div[1]') | 選取 body 元素節點<font color='red'>下</font>的第一個 div 子節。 |
xpath('./body/div[last()]') | 選取 body 元素節點<font color='red'>下</font>的最后一個 div 子節。 |
xpath('./body/div[last()-1]') | 選取 body 元素節點<font color='red'>下</font>的倒數第二個 div 子節。 |
xpath('./body/div[position()-3]') | 選取 body 元素節點<font color='red'>下</font>的前二個 div 子節。 |
xpath('./body/div[@class]') | 選取 body 元素節點<font color='red'>下</font>的所有帶有 class 屬性的 div 子節。 |
xpath("./body/div[@class='content']") | 選取 body 元素節點<font color='red'>下</font>的 class 屬性值為 centent 的 div 子節。 |
2.3"綠葉" —— 節點內容以及屬性
到了這一步,我們已經找到所需內容的節點了。接下來就是獲取該節點中的內容了。Xpath 語法提供了提供節點的文本內容以及屬性內容的功能。
路徑表達式 | 描述 |
---|---|
text() | 獲取節點的本文內容 |
@屬性 | 獲取節點的屬性內容 |
具體用法見以下實例:
路徑表達式 | 描述 |
---|---|
xpath('./a/text()') | 獲取當前節點<font color='purple'>中</font> a 元素節點<font color='red'>中</font>的本文內容 |
xpath('./a/@href') | 獲取當前節點<font color='purple'>中</font> a 元素節點<font color='red'>中</font>的 href 屬性的內容 |
3 lxml 的用法
3.1 安裝 lxml
pip 是安裝庫文件的最簡便的方法,具體命令如下:
pip install lxml
# 如果出現因下載失敗導致安裝不上的情況,可以先啟動 ss 再執行安裝命令
# 或者在終端中使用代理
pip --proxy http://代理ip:端口 install lxml
3.2 使用 lxml
lxml 使用起來是比較簡單的。我們首先要使用 lxml 的 etree 將 html 頁面進行初始化,然后丟給 Xpath 匹配即可。具體用法如下:
from lxml import etree
html = requests.get(url) # 使用 requests 請求網頁
selector = etree.HTML(html.text)
content = selector.xpath('//a/text()')
沒錯,就這短短幾行代碼即可完成信息提取。
值得注意的是:xpath 查找匹配返回的類型有可能是一個值,也有可能是一個存放多個值的列表。這個取決于你的路徑表達式是如何編寫的。
上篇文章:詳解 Requests 庫的用法
推薦閱讀:Python 多進程與多線程