标签:
姓名:郭钟
当前职位:某创业公司担任爬虫工程师
随着互联网信息的不断发展,信息数据的挖掘技术也不断的发展。网络爬虫技术也随之得到了巨大的发展。而对于内容型驱动的网站来说反扒是一件必不可少的事情。很多网站用Jquery加壳、登录验证、限制单位IP每秒请求次数来阻止爬虫窃取数据。所以爬虫的智能性也受到越来大的挑战。特别是国内高匿代理IP资源少的问题给爬虫的技术发展代理巨大挑战。本论文由浅入深剖析一个定向爬虫最为重要的几点,由此得出一个适应现国内爬虫架构的解决方案。
本人在IT技术领域摸爬滚打已有近2年。做过几个Android项目、爬虫也做过3-4个。爬过防爬技术牛逼的豆瓣网、美团,也爬过前程无忧、智联招聘、看准网、百度。所有这些项目经验告诉我:爬虫抓取效率的最大瓶颈在爬虫的架构设计和代理IP两点。所以,借几个项目经验谈谈我对如何做好一个定向爬虫的看法。本文开门见山直击问题所在,并提出解决方案。其他概念叙述不到位的地方还请读者见谅。
1、------------------URL深度优先策略的再优化
2、------------------模拟浏览器内核使抓取更强大
3、------------------适应失效代理ip的管理优化
4、------------------URL上下文使开发业务逻辑更加简单
5、------------------多线程的控制
6、------------------任务的定时问题
7、------------------dump出错页面将使调试更加方便
8、------------------抓取log解决中断抓
9、------------------爬虫的jvm参数优化
10、-----------------建立在hadoop基础上实现分布式
深度优先搜索策略。这种策略的主要思想是,从根节点出发找出叶子节点,以此类推。在一个网页中,选择一个超链接,被链接的网页将执行深度优先搜索,形成单独的一条搜索链。当没有其他超链接时,搜索结束。然而这种自动化URL深度自定义并不能达到业务要求的最佳优先状态。
我们来看传统URL队列模型。图中有一个入口URL“A”解析得到三个跟进URL“B”。传统的抓取顺序如飞机图标标注。每个子入口URL顺序抓取得到“C”。问题出来了。假如需要抓取信息就在C那么第一个C1出来就可去抓取C1的信息了。为什么要迟迟等到B抓取完了才能开始抓取C呢。而且如果根据URL多深度大的话下级URL越积越多。这是对内存的巨大的浪费,所以爬虫容易内存溢出问题也在这里体现了一个方面。
最佳的优化效果得到了一组“B”URL后递归抓取完每个B及下面的子URL”C”。这样就不会产生当前还处理不了但有消耗内存巨大的跟进URL堆积问题。优化后如图。
网页上需要内容都是JS动态加载的。有的甚至是jquery加载后html源码根本无法看到信息数据。解决这种问题唯一的办法是让爬虫自己变成一个没有界面的浏览器。现在java有现成的浏览器驱动库htmlunit可以模拟浏览器,也可以调用现成的火狐、谷歌内核。Python有pyv8的JS引擎、QtWebKit等组件。有了这些组件我们封装一个爬虫带js调用和模拟登陆的就很简单了。
模拟浏览器架构:
1、因为浏览器内核消耗内存较大,需要封装一个内核池WebDriberPool来管理所有的内核,统一创建和关闭。
2、下载器必须对外提供设置JS代码的接口
3、当下载网页时需要执行JS自动执行先前设置好的JS代码
4、当爬行模式为代理IP模式的时候自动向IP管理取得IP
假如我们已经购买了某家代理IP,并供应商提高API提取方式。
1需要代理IP网络缓冲池
2需要代理本地缓存池,用以缓存有效IP
3当爬虫提取代理IP时判断缓存是否达到足够使用的量,有则拿缓存好的IP。
4没有缓存则进行网络提取一批代理IP加入到网络缓冲池。
5、爬虫抓取过程也是验证过程。有效代理IP被Cache到缓存池以提供其他请求使用。
6、当请求失败后如果该IP来自缓存则将器IP踢出缓存队列。
7、可设置代理IP失效超时时间。在内存驻留超过时间的IP踢出队列
实现框架图如下:
在开发爬虫的时候发现数据库设计的数据常常在单一页面抓不完整。通常需要几个页面或者需要查询一些业务关联ID。那么对于这些情况一般是重新去数据库查询更新的数据,所以在写DAO层的时候也多了很多方法。业务逻辑复杂度也越来越高。案例如图:
上下文类架构:
1、建立为每个初始URL封装startRequest
2、提供支持线程并发安全的全局缓存的键值表
3、每个Request的实例化转移到上下文类当中
4、爬虫抓取时可向键值表中put属性
5、子页面需要时可以通过Requst 取得上下文实例取得相应属性
6、爬虫任务完成。自动清除所有上下文缓存信息。
爬虫线程工作周期很长如果在程序的某个阶段发生异常那么以为着之前程序所做的工作将会前功尽弃。所有爬虫多线程应划分为两个部分。一个是专门请求URL下载html页面的线程。这部分线程耗时长,各自工作独立,不需要较多使用synchronized ,容易发生异常。另一部分是“离线处理“的线程就是把下载回来的数据进行抽取、清洗、封装、存储的线程。这部分线程跟业务关系紧密,处理快耗时短。工作有时需要协调、需要使用较多synchronized和线程安全的缓冲队列。建议下载线程和离线处理线程数量比例的4:1。
最后还有一个线程生命周期和爬虫任务相同,它不断轮询URL队列最新的URL,把URL分配给下载线程。
爬虫任务周期长或者周期短。或业务需要爬虫重复去抓取一些动态数据。这时就需要定时机制。实际上Java的Timer已经提供了定时的功能。我们只需要将启动爬虫的程序交给定时器。爬虫任务完成继而向定时器设置下次启动时间。随后休眠,等待再次启动。
结合多线程控制和任务定时管理得出如下架构图:
标签:
原文地址:http://blog.csdn.net/u012572945/article/details/44521111