Python裏經常使用的網頁解析庫有BeautifulSoup和lxml.html,其中前者可能更知名一點吧,熊貓開始也是使用的BeautifulSoup,可是發現它實在有幾個問題繞不過去,所以最後採用的仍是lxml: javascript
1. BeautifulSoup太慢。熊貓原來寫的程序是須要提取不定網頁裏的正文,所以須要對網頁進行不少DOM解析工做,通過測試能夠認定BS平均比lxml要慢10倍左右。緣由應該是libxml2+libxslt的原生C代碼比python仍是要更快吧 html
2. BS依賴python自帶的sgmllib,可是這個sgmllib至少有兩個問題。首先,它解析「class=個人CSS類」這樣的字符串會有問題,參考下面的代碼就知道了。 java
from BeautifulSoup import BeautifulSoup html = u'<div class=個人CSS類>hello</div>' print BeautifulSoup(html).find('div')['class']
打印出來的結果是長度爲零的字符串,而不是「個人CSS類」。 python
不過這個問題能夠經過外圍代碼來解決,只要改寫一下sgmllib的attrfind這個查找元素屬性的正則就行,能夠改爲 多線程
sgmllib.attrfind = re.compile(r'\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*(\'[^\']*\'|"[^"]*"|[^\s^\'^\"^>]*))?')
這個問題能夠說是網頁書寫不規範形成的,也不能怨sgmllib吧,可是這和BS原來但願能解析格式很差的HTML的宗旨是相違背的。 dom
可是第二個問題就比較要命了,參看下面的示例代碼。 函數
from BeautifulSoup import BeautifulSoup html = u'<a onclick="if(x>10) alert(x);" href="javascript:void(0)">hello</a>' print BeautifulSoup(html).find('a').attrs
打印出來的結果是: 學習
[(u'onclick', u'if(x>10) alert(x);')]
顯然其中的href屬性被拋棄了,緣由就是sgmllib庫在解析屬性的時候一旦遇到了>等特殊符號就會結束屬性的解析,要解決這個問題,只能修改sgmllib中SGMLParser的parse_starttag代碼,找到292行,即k = match.end(0)這一行,添加下面的代碼便可: 測試
if k > j: match = endbracket.search(rawdata, k+1) if not match: return -1 j = match.start(0)
所以對比起來lxml會好不少,也許在解析某些HTML的時候真的會出問題,可是就如今使用的狀況來講仍是挺好的。並且lxml的xpath感受真的很棒,幾年前在折騰ASP.NET/Web Service的時候學習過XPath/XSLT之類的東西,可是實用其實挺少的,此次用lxml的xpath,能速度搞定一大堆較繁瑣的元素查找,簡直太爽了。例如要查找全部有name屬性和content屬性的meta元素: spa
dom.xpath('.//meta[@name][@content]')下面是判斷元素x是不是元素y的祖節點的代碼:
x in y.xpath('ancestor-or-self::*')
此外,lxml裏還支持string-length、count等XPath 1.0的函數(參見XPath and XSLT with lxml)。不過2.0的函數,如序列操做的函數就不行了,這須要底層libxml2和libxslt庫的升級才行。
固然,lxml也有它本身的問題,那就是多線程方面貌似有重入性問題,若是須要解析大量網頁,那隻能啓動多個進程來試試了。