前面的文章,我們已經(jīng)學(xué)會正則表達(dá)式以及 BeautifulSoup庫的用法仑氛。我們領(lǐng)教了正則表達(dá)式的便捷械荷,感受 beautifulSoup 的高效。本文介紹也是內(nèi)容提取的工具 —— Xpath版仔,它一般和 lxml 庫搭配使用游盲。所以,我稱這兩者為“干將莫邪”邦尊。
1 Xpath 和 lxml
- Xpath
XPath即為XML路徑語言背桐,它是一種用來確定XML(標(biāo)準(zhǔn)通用標(biāo)記語言的子集)文檔中某部分位置的語言。XPath 基于 XML 的樹狀結(jié)構(gòu)蝉揍,提供在數(shù)據(jù)結(jié)構(gòu)樹中找尋節(jié)點(diǎn)的能力链峭。
Xpath 原本是用于選取 XML 文檔節(jié)點(diǎn)信息。XPath 是于 1999 年 11 月 16 日 成為 W3C 標(biāo)準(zhǔn)又沾。因其既簡單方便又容易弊仪,所以它逐漸被人說熟知。
- lxml
lxml 是功能豐富又簡單易用的杖刷,專門處理 XML 和 HTML 的 Python 官網(wǎng)標(biāo)準(zhǔn)庫励饵。
2 Xpath 的語法
正則表達(dá)式的枯燥無味又學(xué)習(xí)成本高,Xpath 可以說是不及其萬分之一滑燃。所以只要花上 10 分鐘役听,掌握 Xpath 不在話下。Xpath 的語言以及如何從 HTML dom 樹中提取信息表窘,我將其歸納為“主干 - 樹支 - 綠葉”典予。
2.1 “主干” —— 選取節(jié)點(diǎn)
抓取信息,我們需知道要從哪里開始抓取乐严。因此瘤袖,需要找個(gè)起始節(jié)點(diǎn)。Xpath 選擇起始節(jié)點(diǎn)有以下可選:
表達(dá)式 | 描述 |
---|---|
nodename | 選取標(biāo)簽節(jié)點(diǎn)的所有子節(jié)點(diǎn)昂验。 |
/ | 從根節(jié)點(diǎn)選取捂敌,html DOM 樹的節(jié)點(diǎn)就是 html。 |
// | 從匹配選擇的當(dāng)前節(jié)點(diǎn)選擇文檔中的節(jié)點(diǎn)既琴,而不考慮它們的位置占婉。 |
. | 選擇當(dāng)前節(jié)點(diǎn),一般用于二級提取呛梆。 |
.. | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)锐涯,在二級提取時(shí)用到。 |
@ | 選取屬性填物。 |
我們通過以下實(shí)例來了解其用法:
路徑表達(dá)式 | 描述 |
---|---|
xpath('//div') | 選取 div 元素的所有子節(jié)點(diǎn)纹腌。 |
xpath('/div') | 選取 div 元素作為根節(jié)點(diǎn)霎终。如果同級有多個(gè) div ,那么所有 div 都會被選為根節(jié)點(diǎn)升薯。 |
xpath('/div/span') | 選取屬于 div 元素<font color='red'>下</font>所有 span 元素節(jié)點(diǎn)莱褒。如果 span 有多個(gè),也會被選中涎劈。 |
xpath('//div') | 選取所有 div 元素節(jié)點(diǎn)广凸,不管它們在文檔的位置。 |
xpath('//div/span') | 選取 div 元素<font color='red'>下</font>的所有 span 元素節(jié)點(diǎn)蛛枚,不管位于 div 之下的什么位置 |
xpath("http://@[class='content']") | 選取包含屬性 class 的值為 content 的節(jié)點(diǎn)谅海,不管是 div 元素還是其他元素 |
xpath("http://@[id='center']") | 選取屬性 id 的值為 center 的節(jié)點(diǎn),不管是 div 元素還是其他元素 |
如果你對于提取節(jié)點(diǎn)沒有頭緒的時(shí)候蹦浦,可以使用通配符來暫時(shí)替代扭吁。等查看輸出內(nèi)容之后再進(jìn)一步確認(rèn)。
路徑表達(dá)式 | 描述 |
---|---|
xpath('/div/*') | 選取 div 元素節(jié)點(diǎn)<font color='red'>下</font>的所有節(jié)點(diǎn) |
xpath('/div[@*]') | 選取所有帶屬性的 div 元素節(jié)點(diǎn) |
2.2 “分支” —— 關(guān)系節(jié)點(diǎn)與謂語
這一步的過程其實(shí)是通過起點(diǎn)一步步來尋找最終包含我們所需內(nèi)容的節(jié)點(diǎn)盲镶。我們有時(shí)需要使用到相鄰節(jié)點(diǎn)信息侥袜。因此,我們需要了解關(guān)系節(jié)點(diǎn)或者謂語溉贿。
- 關(guān)系節(jié)點(diǎn)
一般而言枫吧,DOM 樹中一個(gè)普通節(jié)點(diǎn)具有父節(jié)點(diǎn)、兄弟節(jié)點(diǎn)宇色、子節(jié)點(diǎn)九杂。當(dāng)然也有例外的情況。這些有些節(jié)點(diǎn)比較特殊宣蠕,可能沒有父節(jié)點(diǎn)尼酿,如根節(jié)點(diǎn);也有可能是沒有子節(jié)點(diǎn)植影,如深度最大的節(jié)點(diǎn)。Xpath 也是有支持獲取關(guān)系節(jié)點(diǎn)的語法涎永。
關(guān)系 | 路徑表達(dá)式 | 描述 |
---|---|---|
parent(父) | xpath('./parent::*') | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
ancestor(先輩) | xpath('./ancestor::*') | 選取當(dāng)前節(jié)點(diǎn)的所有先輩節(jié)點(diǎn)思币,包括父、祖父等 |
ancestor-or-self(先輩及本身) | xpath('./ancestor-or-self::*') | 選取當(dāng)前節(jié)點(diǎn)的所有先輩節(jié)點(diǎn)以及節(jié)點(diǎn)本身 |
child(子) | xpath('./child::*') | 選取當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn) |
descendant(后代) | xpath('./descendant::*') | 選取當(dāng)前節(jié)點(diǎn)的所有后代節(jié)點(diǎn)羡微,包括子節(jié)點(diǎn)谷饿、孫節(jié)點(diǎn)等 |
following | xpath('./following ::*') | 選取當(dāng)前節(jié)點(diǎn)結(jié)束標(biāo)簽后的所有節(jié)點(diǎn) |
following-sibing | xpath('./following-sibing::*') | 選取當(dāng)前節(jié)點(diǎn)之后的兄弟節(jié)點(diǎn) |
preceding | xpath('./preceding::*') | 選取當(dāng)前節(jié)點(diǎn)開始標(biāo)簽前的所有節(jié)點(diǎn) |
preceding-sibling | xpath('./parent::*') | 選取當(dāng)前節(jié)點(diǎn)之前的兄弟節(jié)點(diǎn) |
self(本身) | xpath('./self::*') | 選取當(dāng)前節(jié)點(diǎn)本身 |
- 謂語
謂語用來查找某個(gè)特定的節(jié)點(diǎn)或者包含某個(gè)指定的值的節(jié)點(diǎn)琅催。同時(shí)狞甚,它是被嵌在方括號中的。
路徑表達(dá)式 | 描述 |
---|---|
xpath('./body/div[1]') | 選取 body 元素節(jié)點(diǎn)<font color='red'>下</font>的第一個(gè) div 子節(jié)加袋。 |
xpath('./body/div[last()]') | 選取 body 元素節(jié)點(diǎn)<font color='red'>下</font>的最后一個(gè) div 子節(jié)盯蝴。 |
xpath('./body/div[last()-1]') | 選取 body 元素節(jié)點(diǎn)<font color='red'>下</font>的倒數(shù)第二個(gè) div 子節(jié)毅哗。 |
xpath('./body/div[position()-3]') | 選取 body 元素節(jié)點(diǎn)<font color='red'>下</font>的前二個(gè) div 子節(jié)听怕。 |
xpath('./body/div[@class]') | 選取 body 元素節(jié)點(diǎn)<font color='red'>下</font>的所有帶有 class 屬性的 div 子節(jié)。 |
xpath("./body/div[@class='content']") | 選取 body 元素節(jié)點(diǎn)<font color='red'>下</font>的 class 屬性值為 centent 的 div 子節(jié)虑绵。 |
2.3"綠葉" —— 節(jié)點(diǎn)內(nèi)容以及屬性
到了這一步尿瞭,我們已經(jīng)找到所需內(nèi)容的節(jié)點(diǎn)了。接下來就是獲取該節(jié)點(diǎn)中的內(nèi)容了翅睛。Xpath 語法提供了提供節(jié)點(diǎn)的文本內(nèi)容以及屬性內(nèi)容的功能声搁。
路徑表達(dá)式 | 描述 |
---|---|
text() | 獲取節(jié)點(diǎn)的本文內(nèi)容 |
@屬性 | 獲取節(jié)點(diǎn)的屬性內(nèi)容 |
具體用法見以下實(shí)例:
路徑表達(dá)式 | 描述 |
---|---|
xpath('./a/text()') | 獲取當(dāng)前節(jié)點(diǎn)<font color='purple'>中</font> a 元素節(jié)點(diǎn)<font color='red'>中</font>的本文內(nèi)容 |
xpath('./a/@href') | 獲取當(dāng)前節(jié)點(diǎn)<font color='purple'>中</font> a 元素節(jié)點(diǎn)<font color='red'>中</font>的 href 屬性的內(nèi)容 |
3 lxml 的用法
3.1 安裝 lxml
pip 是安裝庫文件的最簡便的方法,具體命令如下:
pip install lxml
# 如果出現(xiàn)因下載失敗導(dǎo)致安裝不上的情況捕发,可以先啟動 ss 再執(zhí)行安裝命令
# 或者在終端中使用代理
pip --proxy http://代理ip:端口 install lxml
3.2 使用 lxml
lxml 使用起來是比較簡單的疏旨。我們首先要使用 lxml 的 etree 將 html 頁面進(jìn)行初始化,然后丟給 Xpath 匹配即可扎酷。具體用法如下:
from lxml import etree
html = requests.get(url) # 使用 requests 請求網(wǎng)頁
selector = etree.HTML(html.text)
content = selector.xpath('//a/text()')
沒錯(cuò)檐涝,就這短短幾行代碼即可完成信息提取。
值得注意的是:xpath 查找匹配返回的類型有可能是一個(gè)值霞玄,也有可能是一個(gè)存放多個(gè)值的列表骤铃。這個(gè)取決于你的路徑表達(dá)式是如何編寫的。
上篇文章:詳解 Requests 庫的用法
推薦閱讀:Python 多進(jìn)程與多線程