标签:
转自:http://www.cnblogs.com/rzhang/archive/2011/12/29/python-html-parsing.html
Python里常用的网页解析库有BeautifulSoup和lxml.html,其中前者可能更知名一点吧,熊猫开始也是使用的BeautifulSoup,但是发现它实在有几个问题绕不过去,因此最后采用的还是lxml:
1. BeautifulSoup太慢。熊猫原来写的程序是需要提取不定网页里的正文,因此需要对网页进行很多DOM解析工作,经过测试可以认定BS平均比lxml要慢10倍左右。原因应该是libxml2+libxslt的原生C代码比python还是要更快吧
2. BS依赖python自带的sgmllib,但是这个sgmllib至少有两个问题。首先,它解析“class=我的CSS类”这样的字符串会有问题,参考下面的代码就知道了。
1 2 3 | from BeautifulSoup import BeautifulSoup html = u ‘<div class=我的CSS类>hello</div>‘ print BeautifulSoup(html).find( ‘div‘ )[ ‘class‘ ] |
打印出来的结果是长度为零的字符串,而不是“我的CSS类”。
不过这个问题可以通过外围代码来解决,只要改写一下sgmllib的attrfind这个查找元素属性的正则就行,可以改成
1 | sgmllib.attrfind = re. compile (r ‘\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*(\‘[^\‘]*\‘|"[^"]*"|[^\s^\‘^\"^>]*))?‘ ) |
这个问题可以说是网页书写不规范造成的,也不能怨sgmllib吧,但是这和BS原来希望能解析格式不好的HTML的宗旨是相违背的。
但是第二个问题就比较要命了,参看下面的示例代码。
1 2 3 | 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 |
打印出来的结果是:
1 | [(u ‘onclick‘ , u ‘if(x>10) alert(x);‘ )] |
显然其中的href属性被抛弃了,原因就是sgmllib库在解析属性的时候一旦遇到了>等特殊符号就会结束属性的解析,要解决这个问题,只能修改sgmllib中SGMLParser的parse_starttag代码,找到292行,即k = match.end(0)这一行,添加下面的代码即可:
1 2 3 4 | 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元素:
1 | dom.xpath( ‘.//meta[@name][@content]‘ ) |
1 | x in y.xpath( ‘ancestor-or-self::*‘ ) |
此外,lxml里还支持string-length、count等XPath 1.0的函数(参见XPath and XSLT with lxml)。不过2.0的函数,如序列操作的函数就不行了,这需要底层libxml2和libxslt库的升级才行。
当然,lxml也有它自己的问题,那就是多线程方面貌似有重入性问题,如果需要解析大量网页,那只能启动多个进程来试试了。
转:Python网页解析:BeautifulSoup vs lxml.html
标签:
原文地址:http://www.cnblogs.com/zhang-pengcheng/p/4731536.html