基于webmagic的爬虫项目经验小结
大概在1个月前,利用webmagic做了一个爬虫项目,下面是该项目的一些个人心得,贴在这里备份:
一、为什么选择webmagic?
说实话,开源的爬虫框架已经很多了,有各种语言(比如:python、java)实现的,有单机的,还有大型分布式的,多达上百种,详情可见:
http://www.oschina.net/project/tag/64/spider?lang=0&os=0&sort=view&
github上随手搜索一下spider之类的关键字,也不计其数,如何选择呢?
我的标准其实很简单:
a) 要有一定量的使用群体(即:用的人越多越好),有人实际在用的项目,才会有生命力
b) 文档要全(没有文档或文档不全的项目,学起来太费劲)
c) 使用起来要简单,越傻瓜越好(否则,如果要先安装这,安装那,弄一堆依赖的东西,太花时间了)
d) 爬虫的主要功能要有,比如:支持多线程,url自动去重复,html解析方便(至少要能支持css选择器,xpath选择器,正则表达式等常见的解析方式
e) 架构不要太庞大,越轻巧越好,简单的设计,意味着扩展起来比较容易,有些功能如果要自行扩展,直接继承一个类就完事了
把这些因素考虑进去后,综合下来,选择了webmagic,作者很用心,有一个很完整的教科书式的在线文档:http://webmagic.io/docs/zh/ 基本上花半天时间看完,就明白爬虫是怎么回事了。
目标网站的内容变了,如何设计自 己的更新爬取策略,这也是要认真考虑的。
通常最终保存内容的db小型项目中只会有一个,尽管爬虫支持多线程并发爬取,可以分布式的高效狂爬,但是db是一个慢速的IO瓶颈,如果把 "爬取->解析->入库"全放在一个模块中按顺序同步处理,最后的结果相当于前面有几个水管收集水源,但是最后进入水库的总管道不给力,整体 的蓄水效率还是很慢。
分开之后,爬取模块可以同时部署多个,然后将得到的html集中存储在1个目录下,再按子目录存储(比如:一个大型网站,会有很多分站,可以实例A 爬上海站,实例B爬北京站...),这样可以尽可能快的把内容先捞回来。然后由解析模块,再到这个目录下将文件取出来慢慢解析入库,解析成功后将原始文件 删除(或移到其它目录备份,这个看情况而定),如果代码有问题,比如解析规则有bug,导致某些页面解析失败,因为原始html文件已经在本机存储,修正 解析的bug后,可以再试重新解析失败的文件,而不需要重新爬取。
至于爬取模块完成后,如何通知解析模块去开始解析入库,有很多办法,比如消息队列,zookeeper订阅节点状态变化,或者在某个目录下放置一个标记文件 之类的都可以。
三、如何更有效的更新爬取
通常爬取时,会先从一个所谓的"种子URL"层层引导,直到发现最终的目标url,首次爬取时,可以将最终页面的url及http的返回码 (404,500,200之类)记录下来,下次更新爬取时,直接重新爬取这些http状态为200的最终页面即可,这样省去了再次从seed页面层层分析 的过程。(当然,这个要看自身项目的特点,如果seed页的内容本身会周期性的变化,那就省不了从seed页重新爬取的过程)
四、其它一些可能会遇到的问题
a) xpath的问题
webmagic提供的xpath解析工具,不支持xpath2.0的一些高级特性,比如:查找父节点之类,解析时可以考虑引入其它一些第三方开源 库,比如dom4j来处理,反正html内容已经保存到硬盘上了,想咋解析都行(但是dom4j也有一个缺点,返回的html必须是严格符合xml规范 的,有些网页的html源代码,标签没有正常结束,会导致dom4j出错)
b) ajax的问题
有些数据是通过ajax动态请求得到的,在目标网站上并未以a链接的方式明显给出,这种情况可以根据用一些浏览器的开发者工具,观察最终发出去的 ajax请求,如果ajax请求的url是有规律的,可以直接在webmagic中用类似 page.addTargetRequests("xxx") 的方式手动添加。
c) post的问题
webmagic目前的版本,不支持post方式的url爬取,据说以后的版本会考虑,这个暂时可以手动写httpclient来发起post请求,最终拿到数据
d)如何对应有防爬机制的网站
这个没有一劳永逸的办法,具体情况具体分析,
-- 有些网站会限制url访问的频率(比如:同1个ip1分钟内只能访问某个页面N次),这种需要手动在代码里控制下节奏,比如每次http请求后,加入sleep(5000)之类的,
-- 有些网站会根据http请求header中的User-Agent来判断是否是同一款浏览器在恶意刷,这种可以在代码中手动弄一个User-Agent列 表,把市面上的所有User-Agent全加进去,每次请求前,随机从列表中取一个User-Agent,这样看起来,貌似有很多不同的浏览器在访问,显 得真实一点。但是有一点要注意,目前很多大型网站都提供了pc版与移动版的页面,如果在pc浏览器上访问,对方会返回pc版的网页,如果用手机访问,会返 回移动版的页面,很多就是根据User-Agent来判断的(因为PC浏览器与手机浏览器对应的User-Agent信息不同),如果你希望每次爬虫访问 的都是PC版的页面,用代码设置User-Agent时,要小心别弄错了。
-- 有些网站会限制IP,甚至有IP黑名单机制,对于这种出狠招的网站,我们也只能放大招:花点钱,找一群代理服务器,在爬虫代码里,随机切换代理服务器。
Selenium是一款自动化测试工具。它能实现操纵浏览器,包括字符填充、鼠标点击、获取元素、页面切换等一系列操作。总之,凡是浏览器能做的事,Selenium都能够做到。
这里列出在给定城市列表后,使用selenium来动态抓取qunar的票价信息的代码。
参考项目:网络爬虫之Selenium使用代理登陆:爬取去哪儿网站
对于网站有验证码的情况,我们有三种办法:
- 使用代理,更新IP。
- 使用cookie登陆。
- 验证码识别。
- IP代理
- 对于IP代理,各个语言的Native Request API都提供的IP代理响应的API, 需要解决的主要就是IP源的问题了.
- 网络上有廉价的代理IP(1元4000个左右), 我做过简单的测试, 100个IP中, 平均可用的在40-60左右, 访问延迟均在200以上.
- 网络有高质量的代理IP出售, 前提是你有渠道.
- 因为使用IP代理后, 延迟加大, 失败率提高, 所以可以将爬虫框架中将请求设计为异步, 将请求任务加入请求队列(RabbitMQ,Kafka,Redis), 调用成功后再进行回调处理, 失败则重新加入队列. 每次请求都从IP池中取IP, 如果请求失败则从IP池中删除该失效的IP.
大批量爬取目标网站的内容后, 难免碰到红线触发对方的反爬虫机制. 所以适当的告警提示爬虫失效是很有必有的.
一般被反爬虫后, 请求返回的HttpCode为403的失败页面, 有些网站还会返回输入验证码(如豆瓣), 所以检测到403调用失败, 就发送报警, 可以结合一些监控框架, 如Metrics等, 设置短时间内, 告警到达一定阀值后, 给你发邮件,短信等.
当然, 单纯的检测403错误并不能解决所有情况. 有一些网站比较奇葩, 反爬虫后返回的页面仍然是200的(如去哪儿), 这时候往往爬虫任务会进入解析阶段, 解析失败是必然的. 应对这些办法, 也只能在解析失败的时候, 发送报警, 当告警短时间到达一定阀值, 再触发通知事件.
当然这个解决部分并不完美, 因为有时候, 因为网站结构改变, 而导致解析失败, 同样回触发告警. 而你并不能很简单地区分, 告警是由于哪个原因引起的.
高速存储:</b>爬虫系统的速度瓶颈主要就在存储和带宽上,至于存储,选用Hbase而不是MySQL,实测过,导出300+GB的数据,Hbase几分钟搞定,MySQL好几个小时。</li>
<li>PhantomJS、cURL等API,验证码识别。</li>
<li>登陆功能:要爬取微博这种需要登陆才能浏览内容的网站,肯定需要写一个登陆功能的,这里就需要注册很多的僵尸账号了,而且还需要维护僵尸账号的cookie池,很麻烦。</li>
<li><b>采用非人力的方法写爬虫:</b>针对一个网站写爬虫太累了。。来一个任务写一个爬虫,用java写真心累啊。。所以还是想想怎么把爬虫的所有工具和API封装一下吧,用简单的配置和操作就可以完成对当前任务的快速爬取。</li></ol>嗯,以上。
不过到了让你输入验证码的情况,你肯定被反爬程序检测到了咯,方法还是用代理吧...代理+流控。
lamda表达式, memcached分析,redis源码,haproxy,cachecloud,zookeeper,kafka,mycat,mysql,
xpath机制研究
二、如何设计自己的项目架构
选定好一款爬虫开源框架后,就要考虑自己的业务特点,设计自己的项目架构了,大多数用爬虫的人,基本需求其实是类似的:
a) 将目标网站的页面尽可能快速的扒下来
b) 然后解析出有用的内容
c) 落地存储到db中
但凡稍微成熟一些的爬虫的开源框架,步骤a)所需的技术细节,基本上都已经实现了(比如:如何发起http请求,如何进行多线程控制等等),直接拿来用即可,但是解析哪些内容,用什么规则解析,这是每个项目的业务来决定的,需要自己处理,解析完了以后,如何落地,以及目标网站的内容变了,如何设计自己的更新爬取策略,这也是要认真考虑的。
我的个人经验:
项目分成3个模块:
---- 1)spider(爬取模块) ,
---- 2)parser(解析及db入库模块) ,
---- 3)schdule(更新爬取计划任务模块)
模块1)与3)可以打包在同一个jar中集中部署,模块2)单独部署,之所以这样设计,出于以下考虑:
通常最终保存内容的db小型项目中只会有一个,尽管爬虫支持多线程并发爬取,可以分布式的高效狂爬,但是db是一个慢速的IO瓶颈,如果把 "爬取->解析->入库"全放在一个模块中按顺序同步处理,最后的结果相当于前面有几个水管收集水源,但是最后进入水库的总管道不给力,整体的蓄水效率还是很慢。
分开之后,爬取模块可以同时部署多个,然后将得到的html集中存储在1个目录下,再按子目录存储(比如:一个大型网站,会有很多分站,可以实例A爬上海站,实例B爬北京站...),这样可以尽可能快的把内容先捞回来。然后由解析模块,再到这个目录下将文件取出来慢慢解析入库,解析成功后将原始文件删除(或移到其它目录备份,这个看情况而定),如果代码有问题,比如解析规则有bug,导致某些页面解析失败,因为原始html文件已经在本机存储,修正解析的bug后,可以再试重新解析失败的文件,而不需要重新爬取。
至于爬取模块完成后,如何通知解析模块去开始解析入库,有很多办法,比如消息队列,zookeeper订阅节点状态变化,或者在某个目录下放置一个标记文件 之类的都可以。
三、如何更有效的更新爬取
通常爬取时,会先从一个所谓的"种子URL"层层引导,直到发现最终的目标url,首次爬取时,可以将最终页面的url及http的返回码(404,500,200之类)记录下来,下次更新爬取时,直接重新爬取这些http状态为200的最终页面即可,这样省去了再次从seed页面层层分析的过程。(当然,这个要看自身项目的特点,如果seed页的内容本身会周期性的变化,那就省不了从seed页重新爬取的过程)
四、其它一些可能会遇到的问题
a) xpath的问题
webmagic提供的xpath解析工具,不支持xpath2.0的一些高级特性,比如:查找父节点之类,解析时可以考虑引入其它一些第三方开源库,比如dom4j来处理,反正html内容已经保存到硬盘上了,想咋解析都行(但是dom4j也有一个缺点,返回的html必须是严格符合xml规范的,有些网页的html源代码,标签没有正常结束,会导致dom4j出错)
b) ajax的问题
有些数据是通过ajax动态请求得到的,在目标网站上并未以a链接的方式明显给出,这种情况可以根据用一些浏览器的开发者工具,观察最终发出去的ajax请求,如果ajax请求的url是有规律的,可以直接在webmagic中用类似 page.addTargetRequests("xxx")的方式手动添加。
c) post的问题
webmagic目前的版本,不支持post方式的url爬取,据说以后的版本会考虑,这个暂时可以手动写httpclient来发起post请求,最终拿到数据
d)如何对应有防爬机制的网站
这个没有一劳永逸的办法,具体情况具体分析,
-- 有些网站会限制url访问的频率(比如:同1个ip1分钟内只能访问某个页面N次),这种需要手动在代码里控制下节奏,比如每次http请求后,加入sleep(5000)之类的,
-- 有些网站会根据http请求header中的User-Agent来判断是否是同一款浏览器在恶意刷,这种可以在代码中手动弄一个User-Agent列表,把市面上的所有User-Agent全加进去,每次请求前,随机从列表中取一个User-Agent,这样看起来,貌似有很多不同的浏览器在访问,显得真实一点。但是有一点要注意,目前很多大型网站都提供了pc版与移动版的页面,如果在pc浏览器上访问,对方会返回pc版的网页,如果用手机访问,会返回移动版的页面,很多就是根据User-Agent来判断的(因为PC浏览器与手机浏览器对应的User-Agent信息不同),如果你希望每次爬虫访问的都是PC版的页面,用代码设置User-Agent时,要小心别弄错了。
-- 有些网站会限制IP,甚至有IP黑名单机制,对于这种出狠招的网站,我们也只能放大招:花点钱,找一群代理服务器,在爬虫代码里,随机切换代理服务器。