爬虫项目记录–搜索引擎图片爬取
Log my v2ex – crawl pic from SE (1)
记录爬虫项目 – 爬取搜索引擎图片(一)
前言:本文取向于设计分布式爬虫以及爬虫实战相关的思路介绍,关于一些个人工作经验和认识,需求分析,模块拆解去构建爬虫,包含了一些设计思路和解决办法。也欢迎同行交流沟通。
感谢师兄提供的项目需求,也让我体会到数据市场的水有多深,也算是对爬虫和数据有了新的认识。
✓ 0x00 关于爬虫的认识
1. 工程化的爬虫
用一句话来讲,工程化的爬虫系统不同于平时写的脚本,主要原因是由于对于爬虫的监控和调度的逻辑远比写爬虫解析的逻辑要复杂的多。复杂程度和普通的后台系统没有什么区别,甚至比一些后台系统还要复杂。
常见的爬虫框架有很多,如下:
项目名 | 开发语言 | 平台 |
---|---|---|
Heritrix | Java | Linux |
Nutch | Java | Cross-platform |
Scrapy | Python | Cross-platform |
DataparkSearch | C++ | Cross-platform |
Headless chrome crawler | nodejs | |
Colly | Golang | Cross-platform |
GNU Wget | C | Linux |
GRUB | C#, C, Python, Perl | Cross-platform |
ht://Dig | C++ | Unix |
HTTrack | C/C++ | Cross-platform |
ICDL Crawler | C++ | Cross-platform |
mnoGoSearch | C | Windows |
其中,python栈的Scrapy,Java栈的Nutch,Golang栈的Colly, 以及Linux平台的wget想必爬虫工程师基本都有所耳闻。
wget通常只是在Linux上测试环境,或者应用做一些运维方向的包下载。
对比Scrapy和Nutch就很有意思了,Scrapy是python语言的,秉承python的便捷性,能沟通快速、简单、可扩展的方式去抓取你的网页内容。
Nutch是老大哥Apache旗下的成熟的爬虫框架,通常结合Solr索引数据,能够实现简易的搜索引擎。
PS: 之前工作应用Scrapy有段时间,其实关于Scrapy也好很多弊端都是语言上的弊端,毕竟python是解释性语言,
对机器的使用效率肯定没有java高。但是个人觉得Scrapy的设计和实现确实能够满足日常需求。通常玩Python
有个问题就是所谓动态一时爽,重构火葬场。其实这句话在Scrapy的代码上也有体现。尤其是在为了增加各种
插件,效率更是惨不忍睹。Twisted作为事件驱动引擎并发性也并没有那么高效。虽然支持扩展,通常业界都是
redis-scrapy消息队列的通信方式进行分布式爬虫,这块可能也是python的弊端,python虽然是胶水语言,
但是,个人感觉在分布式的领域,python真的不是很擅长。
(以上个人观点)
通常的爬虫框架都具备了如下特点:
- 为了更快速获取到相应数据,爬虫都具有分布式的可扩展性;
- 会应用语言栈中相对高并发的网络的框架去支撑爬虫引擎,提升请求效率;
✓ 0x01 关于爬虫的设计
其实上文提到了要么提高单个应用的网络并发提高网络请求,要么通过分布式的方式调度单体程序,实现提高请求数量。除了这些,能够保证爬虫的完成工作在实际工况下还不能够满足需求。因为现实情况并不是网络爬虫总能爬取到我们想要的数据。要考虑各种爬虫的异常情况,以增加爬虫的健壮性。因此需要明确爬虫成功获取数据都经历了哪些流程,并且需要对这些流程进行监控。
1. 爬虫执行任务的流程模型
获取任务(需要被请求的资源地址)
目标网站发送请求
获取目标网站返回数据
解析目标网站返回数据
持久化的数据
PS: 深挖一些,其实23可以抽象一个模块;其中涉及到一些问题,比如,一个网页的数据并不是直接以静态页或者接口数据的形势返回给爬虫,存在动态页的情况。需要加载js和多次请求才能完成需要持久化的数据,这其中变化比较大。还有可能就是,需要解析并下载视频资源或音频资源或者图片资源。还有可能需要与其他服务配合获取数据,比如通过MITM截取的数据组合可以作为这个阶段要持久化的数据。总之,23过程可以看做是爬虫系统和网页之间交互的过程,最终输出结果是需求的数据。
2. 爬虫执行任务的状态模型
有动作,即为有变化,有变化就会有变化的状态空间,就要能够记录其中的状态。其实以爬虫为例,通常都会记录以上1~5个过程,不考虑特殊聚合数据的情况【js渲染, MITM组合数据】,会根据上述过程进行记录。通常记录过程完成态即可(成功/失败的统计数据),还有完成态的时间。此外,爬虫通常和代理是分不开的。所以,通常也会记录代理的使用情况。以scrapy为例如下:
{'downloader/request_bytes': 212,
'downloader/request_count': 1,
'downloader/request_method_count/GET': 1,
'downloader/response_bytes': 1476,
'downloader/response_count': 1,
'downloader/response_status_count/200': 1,
'elapsed_time_seconds': 0.160045,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2019, 12, 9, 20, 54, 6, 134329),
'log_count/DEBUG': 1,
'log_count/INFO': 10,
'memusage/max': 44896256,
'memusage/startup': 44896256,
'response_received_count': 1,
'scheduler/dequeued': 1,
'scheduler/dequeued/memory': 1,
'scheduler/enqueued': 1,
'scheduler/enqueued/memory': 1,
'start_time': datetime.datetime(2019, 12, 9, 20, 54, 5, 974284)}
3. 爬虫去重
提高爬虫效率,爬虫的去重是必不可少的模块。此时需要区分爬虫是否为增量爬虫,若为增量爬虫,那么去重的粒度就会低一些,或者说去重集合的元素是有存活期的。非增量爬虫,就需要对所有资源进行去重,不仅仅有网页的URL,同时,网页上的资源如果下载也需要进行去重判断。
去重设计也有很多种:
- Bloomfilter 布隆过滤器(优点:节省空间, 缺点:难以复原去重源数据,不能操作过期时间)
- redis Hset Set(Hset 相对 Set 节省点空间,难以操作过期时间)