前一陣參加了乙個python的活動,其間老董的講座是討論網頁爬蟲技術的。其中提到了一下關於頁面解析的問題,他推薦了三種技術。其中有用到libxml2裡的xpath來處理,我就跟令狐談到我曾經也用過這個東東。令狐建議我把這個東東說一下,於是我就寫了這一篇。
慚 愧的是我最初在python裡用xpath時用的不是libxml2,而是乙個不記得是什麼的xml庫。後來因為那個庫不知道為什麼找不到了或者是新版本 不再提供xpath支援等原因,才通過google找到libxml2。就像對pcre的誤解一樣,我原來還以為libxml2是python的庫,後來 才知道它是c的庫,我用的只是python的包裝而已。
很多對xml接觸不多的人,剛開始的印象都會覺得xml不就是一種資料儲存方式嘛,但是實際上在一些極端xmler看來,xml是一種強大的程式語言。不相信的話試著寫乙個xsl就知道了。我對xpath的了解其實也是源於多年寫的乙個程式需要而對xslt作了點研究。
關於xpath的權威資料,當然要算w3c的xpath官方文件 。
下面以乙個例子來說明吧。假設現在我們需要解析這樣乙個「頁面 」,取出其中所有「檔案」段的內容,並且解析為乙個個月份值。這個功能當然也可以用正則表達來式來實現——這也是老董提到的三種技術之一——而且事實上並不會比用xpath麻煩,不過這裡只是舉例說明,就不這麼講究了。
首先,我們需要安裝乙個支援xpath的python庫。目前在libxml2的**上被推薦的python binding是lxml(我以前用的不是這個,不過我也不記得是哪個了),所以現在就以這個為例吧。
安裝方法很簡單: easy_install lxml 即可——什麼?你不知道什麼叫easy_install?那就猛戳peak這裡 學習一下吧。
個麼然後是不是就可以直接用了呢?試試看吧:
import codecs很不幸,可恥滴失敗鳥。因為這個頁面並不是乙個qualified的xhtml——別說中國了,就算在外國也沒有那麼多合格的xhtml頁面,所以還是要可恥滴操起re(正規表示式)來做乙個預處理。from lxml import etree
f=codecs.open("raptor.htm","r","utf-8")
tree=etree.parse(f)
# encoding=utf-8這回就不出錯了嘛。現在被etree所處理的xml其實就是這麼一段內容,很簡單。 在jquery裡要處理這種東西就很簡單了,比如這樣:import codecs
import re
from stringio import stringio
from lxml import etree
f=codecs.open("raptor.htm","r","utf-8")
content=f.read()
f.close()
block_pattern=re.compile(u"(.*?)", re.i | re.s)
m=block_pattern.findall(content)
f=stringio(m[0])
tree=etree.parse(f)
$("li").each(function());但是在python裡要是用re來處理就略麻煩一些,比如這樣:
...那麼換成用xpath要怎麼做呢?是這樣的:m=block_pattern.findall(content)
item_pattern=re.compile(u"(.*?)", re.i | re.s)
items=item_pattern.findall(m[0])
for i in items:
print i
...看上去不比用re簡單嘛,那為什麼要用xpath呢?m=block_pattern.findall(content)
f=stringio(m[0])
tree=etree.parse(f)
nodes=tree.xpath("/ul/li")
for n in nodes:
print n.getchildren()[0].text
這次換個需求,比如這次要取的是id為「_ctl0_leftcolumn-5_categories_catlist__ctl1_linklist__ctl4_link」的鏈結。
jquery版:
$("#_ctl0_leftcolumn-5_categories_catlist__ctl1_linklist__ctl4_link").text();re版:
...xpath版:m=block_pattern.findall(content)
id_pattern=re.compile(u"]*id=/"_ctl0_leftcolumn-5_categories_catlist__ctl1_linklist__ctl4_link/"[^>]*>(.*?)", re.i | re.s)
print id_pattern.findall(m[0])[0]
...對 比三段**與前乙個版本的區別,應該可以看出xpath和jquery對於頁面的解析都是基於xml的語義進行,而re則純粹是基於plain text,如果頁面結構複雜度較高的時候(比如一堆的div來回巢狀之類),設計乙個恰當的re pattern可能會遠比寫乙個xpath要複雜。m=block_pattern.findall(content)
f=stringio(m[0])
tree=etree.parse(f)
nodes=tree.xpath("//a[@id='_ctl0_leftcolumn-5_categories_catlist__ctl1_linklist__ctl4_link']")
print nodes[0].text
當然,xpath的最大問題就是對頁面要求比較高,必須是乙個合格的xhtml,否則還是需要用乙個re去取出合格的片段來進行處理。不知道有沒有什麼庫可以提供頁面的xhtml格式化功能?
xpath在HTML解析中的應用(加強版)
經過一番研究以後才發現原來libxml2其實已經內建了對html的解析 即使是不很規範的html。所以上篇 xpath在xhtml解析中的應用 完全是我學藝不精的產物。囧 不過好處是順便學習到了j7a7c7k7 兄推薦的tidy 用的是令狐提供的 tidylib 這也是個好東東。現在來看如何直接使用...
xhtml1 0模板 在XHTML 2 0中鏈結
xhtml1.0模板 存檔日期 2019年5月15日 首次發布 2005年3月4日 作為web的基本組成部分,超文字鏈結一直是標準化的反覆嘗試的主題,超越了簡單html允許的基本格式。這樣的嘗試可以被描述為在機器處理能力和創作便利之間取得平衡的努力。該領域的最新規範xhtml 2.0可能正確無誤。此...
關於python中的xpath解析定位
爬取的 這裡只針對個別屬性值 例如 別名 下的span標籤文字,發病部位 下的span標籤文字以及 科室 下的span標籤文字 def disease url text get html url tree etree.html text bm tree.xpath ul class informat...