爬蟲隨筆三

xpathcss

特色:樹狀結構、逐層展開、逐層定位、尋找獨立節點。  相似於使用jQuery進行元素搜索html

在進行網頁抓取的時候,分析定位html節點是獲取抓取信息的關鍵,目前我用的是lxml模塊(用來分析XML文檔結構的,固然也能分析html結構), 利用其lxml.html的xpath對html進行分析,獲取抓取信息。
  首先,咱們須要安裝一個支持xpath的python庫。目前在libxml2的網站上被推薦的python binding是lxml,也有beautifulsoup,不嫌麻煩的話還能夠本身用正則表達式去構建,本文以lxml爲例講解。
假設有以下的HTML文檔:
複製代碼
 1 <html>
 2   <body>
 3     <form>
 4       <div id='leftmenu'>
 5         <h3>text</h3>
 6         <ul id=’china’><!-- first location -->
 7           <li>...</li>
 8           <li>...</li>
 9              ......
10         </ul>
11         <ul id=’england’><!-- second location-->
12           <li>...</li>
13           <li>...</li>
14              ......
15         </ul>
16       </div>
17     </form>
18   </body>
19 </html>        
複製代碼

直接使用lxml處理:node

1 import codecs
2 from lxml import etree
3 f=codecs.open("ceshi.html","r","utf-8")
4 content=f.read()
5 f.close()
6 tree=etree.HTML(content)

etree提供了HTML這個解析函數,如今咱們能夠直接對HTML使用xpath了,是否是有點小激動,如今就嘗試下吧。python

 

在使用xpath以前咱們先來看看做爲對照的jQuery和RE。正則表達式

jQuery裏要處理這種東西就很簡單,特別是假如那個ul節點有id的話(好比是<ul id=’china’>):函數

$("#china").each(function(){...});

具體到此處是:網站

$("#leftmenu").children("h3:contains('text')").next("ul").each(function(){...});

找到id爲leftmenu的節點,在其下找到一個內容包含爲」text」的h3節點,再取其接下來的一個ul節點。spa

在python裏要是用RE來處理就略麻煩一些:設計

block_pattern=re.compile(u"<h3>檔案</h3>(.*?)<h3>", re.I | re.S)
m=block_pattern.findall(content)
item_pattern=re.compile(u"<li>(.*?)</li>", re.I | re.S)
items=item_pattern.findall(m[0])
for i in items:
    print i

那麼用xpath要怎麼作呢?其實跟jQuery是差很少的:3d

nodes=tree.xpath("/descendant::ul[@id='china']")

固然,如今沒有id的話也就只能用相似於jQuery的方法了。完整的xpath應該是這樣寫的(注意,原文件中的TAG有大小寫的狀況,可是在XPATH裏只能用小寫):

nodes=tree.xpath(u"/html/body/form/div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")

更簡單的方法就是像jQuery那樣直接根據id定位:

nodes=tree.xpath(u"//div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")

這兩種方法返回的結果中,nodes[0]就是那個「text」的h3節點後面緊跟的第一個ul節點,這樣就能夠列出後面全部的ul節點內容了。

若是ul節點下面還有其餘的節點,咱們要找到更深節點的內容,以下的循環就是把這些節點的文本內容列出:

nodes=nodes[0].xpath("li/a")
for n in nodes:
    print n.text

對比三種方法應該能夠看出xpath和jQuery對於頁面的解析都是基於XML的語義進行,而RE則純粹是基於plain text。RE對付簡單的頁面是沒有問題,若是頁面結構複雜度較高的時候(好比一堆的DIV來回嵌套之類),設計一個恰當的RE pattern可能會遠比寫一個xpath要複雜。特別是目前主流的基於CSS的頁面設計方式,其中大部分關鍵節點都會有id――對於使用jQuery的頁面來講則更是如此,這時xpath相比RE就有了決定性的優點。

 

附錄:基本XPATH語法介紹,詳細請參考XPath的官方文檔

XPATH基本上是用一種相似目錄樹的方法來描述在XML文檔中的路徑。好比用「/」來做爲上下層級間的分隔。第一個「/」表示文檔的根節點(注意,不是指文檔最外層的tag節點,而是指文檔自己)。好比對於一個HTML文件來講,最外層的節點應該是」/html」。

一樣的,「..」和「.」分別被用來表示父節點和本節點。

XPATH返回的不必定就是惟一的節點,而是符合條件的全部節點。好比在HTML文檔裏使用「/html/head/scrpt」就會把head裏的全部script節點都取出來。

爲了縮小定位範圍,每每還須要增長過濾條件。過濾的方法就是用「[」「]」把過濾條件加上。好比在HTML文檔裏使用「/html/body/div[@id='main']」,便可取出body裏id爲main的div節點。

其中@id表示屬性id,相似的還可使用如@name, @value, @href, @src, @class….

而 函數text()的意思則是取得節點包含的文本。好比:<div>hello<p>world</p>< /div>中,用」div[text()='hello']「便可取得這個div,而world則是p的text()。

函數position()的意思是取得節點的位置。好比「li[position()=2]」表示取得第二個li節點,它也能夠被省略爲「li[2]」。

不過要注意的是數字定位和過濾 條件的順序。好比「ul/li[5][@name='hello']」表示取ul下第五項li,而且其name必須是hello,不然返回空。而若是用 「ul/li[@name='hello'][5]」的意思就不一樣,它表示尋找ul下第五個name爲」hello「的li節點。

此外,「*」能夠代替全部的節點名,好比用」/html/body/*/span」能夠取出body下第二級的全部span,而無論它上一級是div仍是p或是其它什麼東東。

而 「descendant::」前綴能夠指代任意多層的中間節點,它也能夠被省略成一個「/」。好比在整個HTML文檔中查找id爲「leftmenu」的 div,能夠用「/descendant::div[@id='leftmenu']」,也能夠簡單地使用「 //div[@id='leftmenu']」。

至於「following-sibling::」前綴就如其名所說,表示同一層的下一個節點。」following-sibling::*」就是任意下一個節點,而「following-sibling::ul」就是下一個ul節點。

 

特殊應用:

//  定位根節點

/  往下層尋找

提取文本內容: /text()

提取屬性內容: /@屬性名

提取html中css屬性的內容,好比   <a title="這是要提取的內容">這不是要提取的內容</a>   則能夠用下面的方式:

titles = selector.xpath('/a/@title')

title = title[0]

print(title[0])

 

以相同的字符開頭的狀況

starts-with(@屬性名稱,屬性字符相同的部分)

selector = etree.HTML(html)

content = selector.xpath('div[start-with(@id,"test")]/text()') 

for each in content:

    print(each)

 

標籤套標籤,提取所有內容

.string()

 

selector = etree.HTML(html)

data = select.xpath('//div[@id="test3"]')[0]

info = data.xpath('string(.)')

content_2 = info.replace('\n', '').replace(' ', '')

print(content_2)

相關文章
相關標籤/搜索