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也有它本身的问题,那就是多线程方面貌似有重入性问题,若是须要解析大量网页,那只能启动多个进程来试试了。