首页  »  beautifulSoup

像CSS选择器一样用BeautifulSoup

较早前写扒虫多,虽然BeautifulSoup很 好用,但厌倦每次都要手动去编写复杂的Dom元素定位语句,突然想,能不能像CSS选择器一样,只给一个表达式就返回我想要的东西?后来在网上找了下没找 着,想了想工作量也不大就自己实现了一个,目前支持ID,Class,标签及属性选择器、支持 space、 >、 + 操作符,基本上已满足大部份定位需求。

代码见附件里的BTSelector.py,另附UnitTest。
常见使用方式是:
注意,本脚本依赖于BeautifulSoup,所以,使用之前请确保你已经安装了。

python 代码
 
  1. from BTSelector import findAll  
  2. soup = BeautifulSoup(htmlContent)  
  3. nodes = findAll('div.navigator #notice',soup)  
  4. # findAll返回的是符合选择条件的Dom 对象列表,实际上是BeautifulSoup的标签或字符类。  

用testCase作为使用示例:
一、上一个复杂一点的用例:

python 代码
 
  1. def testMixSelection(self):  
  2. target = "#header > div#name > a.highlight"  
  3. html = ''' 
  4. <div id="header"> 
  5. <div id="name"> 
  6. <a>test</a> 
  7. <a>right</a> 
  8. <a>ok</a> 
  9. </div> 
  10. <div id="your"> 
  11. </div> 
  12. </div> 
  13. <div id="body">fk 
  14. </div> 
  15. '''  
  16. soup = BeautifulSoup(html)  
  17. ret = findAll(target,soup)  
  18. self.assertEqual(2,len(ret))  

二、再上一个使用位置操作符的用例:

python 代码
 
  1. def testPosition(self):  
  2. target = "h2 + ul > li > a"  
  3. html = ''' 
  4. <h2>title</h2> 
  5. <ul> 
  6. <li><a href="#">nothing</a></li> 
  7. <li><a href="#">ok</a></li> 
  8. <li><a href="#">come on!</a></li> 
  9. </ul> 
  10. '''  
  11. soup = BeautifulSoup(html)  
  12. ret = findAll(target,soup)  
  13. self.assertEqual(3,len(ret))  

更多的见附件的Unittest。希望大家喜欢。

python网络爬虫备忘记

最近写网络爬虫多了,总结出一些心得,记录以备忘记.
网络爬虫的要求有几点:
一. 一个好的网页解释器,python推荐BeautifulSoup,java推荐htmlparser.
二.良好的性能,不能太慢,当遇上网络状况不大好的时候,慢更受不了,所以通常需要同时启动N个网络爬虫进行工作.使用队列可以很好解决这个问题.
三.稳定性,容错性.网络本来就存在不稳定性,连接超时,连接重置等是常见的不稳定现象,网络爬虫程序要充分妥当地处理这些问题,保证爬虫输出结果的完整性.

一.Beautiful Soup 美丽的汤

BeautifulSoup是一个纯py的html(xml)解释器,为许多python开发者所钟爱.其官方网站已有详尽的文档可作参考,再不然,google一下也大把中文文档.所以这里就不对其基本用法多加描述.仅对我走过的弯路进行一下小结.

1 <ul>的contents并不全是<li>,<head>的nextSibling并不一定是<body>
代码说话:

python 代码
  1. >>>from BeautifulSoup import BeautifulSoup  
  2. >>>soup = BeautifulSoup('<ul><li>abc</li><li>def</li></ul>')  
  3. >>> ul = soup.find('ul')  
  4. >>> ul.contents  
  5. [<li>abc</li>, <li>def</li>]  



上面的代码说明<ul>标签下面的子结点全是<li>,试过这小段HTML代码有回车也得到同样的结果,于是我想当然地认为从网 页上扒下来的HTML内容也会得到同样的结果..但实践很快证明并不是这样的,实际上很多网页制作过程中使用了一些不可见字符(如空格,制表符等),这些 字符并不会被忽略,而是被汤看作是NavigableString类型的元素.
所以当我想当然地for li in ul时,很快发现里面有很多杂质而导致错误.更好的方式是自己写个小函数,只取标签即可:

python 代码
  1. def right_tag(tag,name=None):  
  2.     if not name or tag.name == name:  
  3.         return tag  
  4.   
  5. def tag_children(tag,name=None):  
  6.     """ 
  7.     找到所有类型为标签的子元素,可通过name参数指定标签种类. 
  8.     """  
  9.     children = []  
  10.     contents = tag.contents  
  11.     for content in contents:  
  12.         if isinstance(content,Tag):  
  13.             child = right_tag(content,name)  
  14.             if child:  
  15.                 children.append(child)  
  16.     return children  



同样的道理,nextSibling,preSibling也不一定稳妥地帮你找到相邻的标签,下面的函数可以帮你:

python 代码
  1. def next_tag(tag,name=None):  
  2.     """ 
  3.     找到下一个邻近的标签,可指定标签名 
  4.     """  
  5.     current = tag.nextSibling  
  6.     while current:  
  7.         if isinstance(current,Tag):  
  8.             return right_tag(current,name)  
  9.         current = current.nextSibling  


2 编码问题


先复习一下Python的编码机制.
python的内部编码为unicode,如果需要将字符串从编码A转为编码B时,顺序为:

编码A ---(decode)--->内部编码 ---(encode)--->编码B

BeautifulSoup在解释网页文件的时候会把文字转换成unicode的.既然是这样,那应该不用担心编码问题了.友人网的网页是GB2312编 码,在不用指定特殊编码的情况下可以获取到正常的中文,而在我扒当当网的时候,问题出现了,同样是GB2312的编码,但扒下来存在文件的却是乱码.(其 实友人网也有部分乱码,但大部分正常).我检查控制台的输出时发现,BeautifulSoup并没有把中文转换成为真正的双字节unicode,或许说 转换得并不成功,它仅仅是在GB2312的编码串前面加了一个u,如 u'\xe4\xb8\xad\xe6\x96\x87' ,把原本是GB编码的代码假装成了unicode,所以在处理的时候成了乱码.

Beautiful Soup 的文档说:会按顺序尝试不同的编码将你的文档转换为Unicode:

* 可以通过fromEncoding参数传递编码类型给soup的构造器
* 通过文档本身找到编码类型:例如XML的声明或者HTML文档http-equiv的META标签。如果Beautiful Soup在文档中发现编码类型,它试着使用找到的类型转换文档。但是,如果你明显的指定一个编码类型,并且成功使用了编码:这时它会忽略任何它在文档中发 现的编码类型。
* 通过嗅探文件开头的一下数据,判断编码。如果编码类型可以被检测到,它将是这些中的一个:UTF-*编码,EBCDIC或者ASCII。
* 通过chardet 库,嗅探编码,如果你安装了这个库。
* UTF-8
* Windows-1252

我在构造BeautifulSoup对象的时候并没有指定编码,那么看来是第二点起作用了,但效果并不理想,所以最好的方式还是在构造器指定编码.

如soup = BeautifulSoup(page,fromEncoding='gb2312')

实践证时,在构造器指定编码是最佳实践.

二.优化性能

使有队列辅助多线程提速
假如使用单线程去运行网络爬虫,那速度一定让人受不了,由于网络的延时随时可以把一条线程阻塞.
多线程可以充分利用带宽和计算机资源,给网络爬虫提速.

IBM文档中心这篇<<使用python正行线程编程>>正好使用网络爬虫来做为示例讲述如何进行多线程编程以及如何使用队列来优化多线程编码的.很值得参考.

三.容错处理


网络爬虫最常见的错误莫过于连接超时和连接被重置了.而这些错误又是偶然性的,就是说,同一个网页,第一次可能连接超时,第二次可能就正常了.这要求在程 序的设计时考虑到如何这些错误的连接,我的做法是把出问题的连接记录下来,等大队列工作完成后再循环重试有问题的连接,直到全部正常.

另外,关于网络超时的问题.如果超时时间过长的话,网速慢的时候会大大影响网络爬虫的执行速度,所以通常有人希望设置网络连接的超时 值.python2.5的urlib2的urlopen函数并没有接受timeout参数,(尽管python2.6提供了,目前没敢用).不过可以用 socket.setdefaulttimeout()来设定.当然,可以到pythoncn参考一些行者给出的建议.

Meta

关于本博客...

关于黑莓手机、apple、twitter、互联网、web2.0以及生活的碎言碎语。请在twitter上 follow我,欢迎同好者talk to me bbmyth AT gmail.com。博客Hosting在 webfaction。

赞助商链接

我看我听我读

最新评论

标签

python 空间 开发 计划 年假 工作 诗歌 音乐 西片 恐怖 惊变 django mysql rss 文艺片 太阳 彩色 电影 apache 部署 factcgi lighttpd javascript editor MYMeditor sql 日志 java hibernate orm 数据库 英伦 摇滚 原创 中间件 朋友 erlang 并发 函数式编程 旅游 云南 丽江 发呆 学习 编程 技术 lucene 全文搜索 中文分词 乐队 模板 分页 成功 google pagerank 中文 更新 个性化 秋天 互联网 web ext json ajax 事业 职业 读书 开源 香港 澳门 忧郁 冬天 compass dvd 广州 地下 暴力 美学 声音玩具 独立 备份 数据 琐事 博客 生活 体验 卖唱 接口 设计模式 图表 wiki moin 遇窃 air ria 需求 设计 信息 健康 感悟 人生 真诚 life jquery 杭州 灾害 2008 中国 灾难 哀悼日 jmesa grails flex flash 捐赠 scrum 软件过程 快速开发 plone cms nuexo zope 左小诅咒 demo prototpye AMF actionscript 汉诺塔 算法 递归 结婚 感情 opensource 网络 beautifulSoup 管理 大理 香格里拉 休假 鼻炎 许巍 感性 2009 随想 cpug 聚会 出差 北京 api 创业 商城 blackberry 手机 TD 交流 处事 为人 房子 经济 手机仿真 在线服务 嵌入式 海鲜 p2p easymule apple 技巧 thing gtd task gfw vpn 穿墙 代理 软件管理 翻译 mac 英语 caffeine 休眠 搬家 主机 prism firefox mozilla 免费 php codeigniter url blogspot mindmap mindnode htmlparse easyurl 产品 黑莓 rim 试手机 豆瓣 twitter 微博 杂记 时空 亲人 dabr webfaction host 快速查看 safari appale 桌面 snow 升级 leopard finder 权限 glims python主机 合租 ruby主机 快捷键 itunes 时间管理 原型 画图 招聘 hosting 写作 软件 家庭 广州技术沙龙 postgres 云计算 fuckgfw 内容审检 谷歌 chrome linux odbc database freetds R 统计 书签 浏览器 bookmark tinymce 文件管理 分享 忙碌 旅行 马来西亚 图维导图 freemind 工具 pinax develope shell dropbox barcamp

日志分类

友情链接

博客归档

PowerBy