一、前言通過(guò)前面的文章,我們已經(jīng)知道了如何獲取網(wǎng)頁(yè)和下載文件,但是前面我們獲取的網(wǎng)頁(yè)都是未經(jīng)處理的,冗余的信息太多,無(wú)法進(jìn)行分析和利用 這一節(jié)我們就來(lái)學(xué)習(xí)怎么從網(wǎng)頁(yè)中篩選自己需要的信息 說(shuō)到信息篩選我們立馬就會(huì)想到正則表達(dá)式,不過(guò)今天我們不講正則表達(dá)式。因?yàn)閷?duì)于爬蟲(chóng)來(lái)講,正則表達(dá)式太復(fù)雜對(duì)新手十分不友好,而且正則表達(dá)式的容錯(cuò)率差,網(wǎng)頁(yè)有稍微的改動(dòng)就得重新寫(xiě)匹配表達(dá)式,另外正則表達(dá)式可讀性幾乎沒(méi)有。 當(dāng)然,這并不是說(shuō)正則不好,只是正則不適合爬蟲(chóng)和新手。其實(shí)正則是十分強(qiáng)大的,在后面的數(shù)據(jù)清洗里我們會(huì)用到正則。 既然正則不能用,那該用什么呢?別擔(dān)心,python為我們提供了很多解析 html頁(yè)面的庫(kù),其中常用的有: BeautifulSoup類似 jQuery的選擇器,通過(guò) id、css選擇器和標(biāo)簽來(lái)查找元素,xpath主要通過(guò) html節(jié)點(diǎn)的嵌套關(guān)系來(lái)查找元素,和文件的路徑有點(diǎn)像,比如: #獲取 id為 tab的 table標(biāo)簽下所有 tr標(biāo)簽
path = '//table[@id="tab"]//tr'
#和文件路徑對(duì)比
path = 'D:\Github\hexo\source\_posts'
BeautifulSoup和 xpath沒(méi)有好壞優(yōu)劣之分,講 xpath是因?yàn)閭€(gè)人覺(jué)得 xpath更好用一些,后面如果時(shí)間允許的話再講 BeautifulSoup。 現(xiàn)在,讓我們先從 xpath開(kāi)始! 二、xpath的安裝和使用安裝 lxml庫(kù)簡(jiǎn)單的使用在使用 xpath之前,先導(dǎo)入 etree類,對(duì)原始的 html頁(yè)面進(jìn)行處理獲得一個(gè)_Element對(duì)象 我們可以通過(guò)_Element對(duì)象來(lái)使用 xpath #導(dǎo)入 etree類
from lxml import etree
#作為示例的 html文本
html = '''<div class="container">
<div class="row">
<div class="col">
<div class="card">
<div class="card-content">
<a href="#123333" class="box">
點(diǎn)擊我
</a>
</div>
</div>
</div>
</div>
</div>'''
#對(duì) html文本進(jìn)行處理 獲得一個(gè)_Element對(duì)象
dom = etree.HTML(html)
#獲取 a標(biāo)簽下的文本
a_text = dom.xpath('//div/div/div/div/div/a/text()')
print(a_text)
打印結(jié)果: 熟悉 html的朋友都知道在 html中所有的標(biāo)簽都是節(jié)點(diǎn)。一個(gè) html文檔是一個(gè)文檔節(jié)點(diǎn),一個(gè)文檔節(jié)點(diǎn)包含一個(gè)節(jié)點(diǎn)樹(shù),也叫做 dom樹(shù)。 節(jié)點(diǎn)樹(shù)中的節(jié)點(diǎn)彼此擁有層級(jí)關(guān)系。 父(parent)、子(child)和同胞(sibling)等術(shù)語(yǔ)用于描述這些關(guān)系。父節(jié)點(diǎn)擁有子節(jié)點(diǎn)。同級(jí)的子節(jié)點(diǎn)被稱為同胞(兄弟或姐妹)。 from w3school:http://www.w3school.com.cn/htmldom/dom_nodes.asp
另外,我們把距離某個(gè)節(jié)點(diǎn)最近的子節(jié)點(diǎn)叫做它的直接子節(jié)點(diǎn),如下圖所示的 body和 head就是 html的直接子節(jié)點(diǎn) 了解了 html結(jié)構(gòu)之后我們?cè)賮?lái)看 xpath的使用。 首先,我們通過(guò) etree.HTML( )來(lái)生成一個(gè)_Element對(duì)象,etree.HTML() 會(huì)將傳入的文本處理成一個(gè) html文檔節(jié)點(diǎn)。這樣就能保證我們總是能獲得一個(gè)包含文檔節(jié)點(diǎn)的_Element對(duì)象。 在節(jié)點(diǎn)樹(shù)中,頂端節(jié)點(diǎn)被稱為根(root) 每個(gè)節(jié)點(diǎn)都有父節(jié)點(diǎn)、除了根(它沒(méi)有父節(jié)點(diǎn)) 一個(gè)節(jié)點(diǎn)可擁有任意數(shù)量的子 同胞是擁有相同父節(jié)點(diǎn)的節(jié)點(diǎn)
xpath語(yǔ)法//div[@classs], //a[@x]:選擇具有 class屬性的 div節(jié)點(diǎn)、選擇具有 x屬性的 a節(jié)點(diǎn) //div[@class="container"]:選擇具有 class屬性的值為 container的 div節(jié)點(diǎn)
a / b :‘/’在 xpath里表示層級(jí)關(guān)系,左邊的 a是父節(jié)點(diǎn),右邊的 b是子節(jié)點(diǎn),這里的 b是 a的直接子節(jié)點(diǎn) a // b:兩個(gè) / 表示選擇所有 a節(jié)點(diǎn)下的 b節(jié)點(diǎn)(可以是直接子節(jié)點(diǎn),也可以不是),在上面的例子中我們要選擇 a標(biāo)簽是這樣寫(xiě)的 a_text = dom.xpath('//div/div/div/div/div/a/text()')
#用 //
a_text = dom.xpath('//div//a/text()')
#如果 div標(biāo)簽下有兩個(gè) a標(biāo)簽,那么這兩個(gè) a標(biāo)簽都會(huì)被選擇(注意兩個(gè) a標(biāo)簽并不一定是兄弟節(jié)點(diǎn))
#比如下面的例子中的兩個(gè) a標(biāo)簽都會(huì)被選擇 因?yàn)檫@兩個(gè) a標(biāo)簽都是 div的子節(jié)點(diǎn)
'''<div class="container">
<div class="row">
<div class="col">
<div class="card">
<a href="#123332" class="box">
點(diǎn)擊我
</a>
<div class="card-content">
<a href="#123333" class="box">
點(diǎn)擊我
</a>
</div>
</div>
</div>
</div>
</div>'''
[@]:選擇具有某個(gè)屬性的節(jié)點(diǎn) //a[contains(text(), "點(diǎn)")]:選擇文本內(nèi)容里含有 “點(diǎn)” 的 a標(biāo)簽,比如上面例子中的兩個(gè) a標(biāo)簽 //a[contains(@id, "abc")]:選擇 id屬性里有 abc的 a標(biāo)簽,如 #這兩條 xpath規(guī)則都可以選取到例子中的兩個(gè) a標(biāo)簽
path = '//a[contains(@href, "#123")]'
path = '//a[contains(@href, "#1233")]'
//a[contains(@y, "x")]:選擇有 y屬性且 y屬性包含 x值的 a標(biāo)簽
總結(jié)使用 xpath之前必須先對(duì) html文檔進(jìn)行處理 html dom樹(shù)中所有的對(duì)象都是節(jié)點(diǎn),包括文本,所以 text()其實(shí)就是獲取某個(gè)標(biāo)簽下的文本節(jié)點(diǎn) 通過(guò)_Element對(duì)象的 xpath方法來(lái)使用 xpath 注意!?。Element.xpath( path) 總是返回一個(gè)列表
|