xpathcss
特色:樹狀結構、逐層展開、逐層定位、尋找獨立節點。 相似於使用jQuery進行元素搜索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) |