爬虫技术难学吗?作为一个过来人给出一些经验之谈
总结一下自己的一些爬虫的经验。搞爬虫的初衷就是解决自己站点内容来源的问题,这过程中采集过很多个网站,过程中主要使用的工具从前期的scrapy
,后面工作中也使用过phpspider
,后面接触到golang语言,也自己据它实现过rpc
形式的分布式爬虫
。
scrapy
使用可以说是频次最高的,在居理的时候,基于scrapy
和django-scrapy-admin
改版过可实现UI
界面按点操作的高度灵活、自定义的爬虫。这过程中也接触过很多解决不了问题,引入的解决被ban
的新思路,比如说使用phantomJS
做自动化测试的时候,那时候好多人都写相关的文章,特别在测试工程师当中,使用频次相当高了。后面因为没人维护该开源程序了,流行度下降,取而代之的是操作chrome
的headless
无头浏览器,针对一些需要交互操作才能获取到数据的抓取,这就是绕不开的应对途径了。自己之前文章中分享过,我原来解决这种需要交互操作的思路还比较顽固,当时因为使用scrapy
习惯了,也没有深究如何把selenium
类的工具引入到scrapy
当中,所以就喜欢把玩execJS
,构建在js
中执行的路径,然后触发执行,但是基于js执行,很多都是进行了重新的加密封包,甚至自己把js
脚本不被随意执行,给js
构建了独立的执行引擎,这就更加大了破解环境,解决可执行js文件的问题,后面发现selenium
这种,当时直呼过瘾。关于我用无头浏览器解决抓取某国外站点文章,进入搭建起的人工智能翻译系统,对文章进行伪原创的文章之前分享过,感兴趣的可以找找历史记录。后面又接触到了google
基于chrome
直接推出的puppeteer
,感觉效率更高,而且是谷歌自己推出来的,所以使用的可控性更强,不像使用selenium
之类的还要配置无头浏览器路径,还要python
的selenium
包支持的chrome或者firefox
无头浏览器版本一致,总之,如果涉及到必须要交互才能解决的抓取或者测试场景,还是很推荐使用puppeteer
的。
针对常规页面的抓取,用scrapy
也是有很多技巧的,现在回看一下,对于文本的精细处理,最好用的还是正则表达式,像BeautifulSoup
、像requests
、像urllib|urllib2
等等可以进行使用,但是效率相对比较低,当然如果采集的数据量不是很大,哪个用的顺手用哪个就好了。我在用scrapy
过程中使用xpath
比较多,因为chrome
浏览器安装扩展之后,直接支持copy xapth
出来,这样你只要稍微改改,就能把列表页、分页部分、详情页一些精细的部分给提取出来。但是更精细的操作,比如说在pipeline.py
文件中,scrapy
要在其中自定义入库前的处理,或者针对图片的精细化处理,比如说scrapy
采集过来的数据是在full
目录下,但是你采集源文章主体部分,一般都是相应开源程序,或者开源程序的改版程序、或者是自主开发程序,但是图片路径之类的,都是按日期生成的,这个你采集过来图片,如果不按着人家路径来组织,或者在人家文章里按full
前缀scrapy
抓取过来路径进行替换,那么,文章采集过来就废了,只有文字部分被保留,但是针对现在的文章,没几张图片,特别用于消磨时间,那简直完全读不下去,这个时候,正则表达式就是最好用的工具了,几乎能解决类似的所有问题,而且效率特别高,所以特别推荐对于正则表达式掌握透彻一点,这样对于你高效抓取数据都是颇为有益的。
正文中图片的路径,用相对路径方式替换掉源站的域名,如果图片存储在web
对应于相对路径位置下一般就可以正常访问到了,但是你还要让图片真的按指定路径给下载下来,如果scrapy中不你重写file_path
方法,不重写item_complete
方法,我上面也提到了,默认会下载到full
目录下,所以就要重写我说这两个方法。我前期使用比较多的方式是在item_complete
重写,再用shutil
进行复制或移动操作,比较高效。当然后面也尝试过重写file_path
方法也比较好用,但是因为加水印之类的操作,我想在一个环节里都完成,而我习惯了在item_complete
中完成这些工作,所以使用哪种方式就看个人爱好了。
后面就渐渐使用到分布式爬虫了。分布式爬虫的思路其实很好理解,就是各司其职,这样干特定工作的部分你就可以不断的加强。比如说scrapy
的分布式原理,就是把抓取路径放到redis
队列里,这样的好处显而易见,主要由redis
自身单线程内存服务器特点决定的,可以高效交互,不像你用mysql
还要解决多端访问时候上下文切换、线程锁等问题,可以不断放大使用端数量。这样之前可能把单台服务器性能开到最大,顶多来100个线程来读redis
中的数据进行处理,但是由于redis
的引入,你就可以北京、上海、杭州每个地方都开10台机器来处理这些请求,那么同一时间点就有3000个线程可以来处理,之前一天完成的抓取工作,现在半个小时就可以完成了,这就是分布式爬虫的意义。用golang
来实现分布式爬虫也是一样的原理,把专门写数据库的服务抽象出来、把专门做列表抓取的服务抽象出来、把专门做详情页抓取的服务抽象出来,由于是rpc
服务,所以你可以每种服务开n多个台服务器,只做列表抓取、只做详情抓取、只做写库服务,这样就能不断动态扩容,把各服务数量控制在能最大化榨干各服务最大性能的层面,从而达到降本增效的目的。我觉得用rpc
这种更高精度的抽象,比scrapy
使用redis
这种还要更高效,因为只把取链接分布式了,但是如果你其它执行服务,比如说使用bloomfilter
进行重复数据过滤,把图片放到队列进行抓取,把详情放到线程进行执行入库,如果最终操作的还是同一个数据库,同一个过滤服务,那redis
引入的意义就不大了,阻塞依旧会出现。还是要把服务像rpc
的分布式爬虫一样各种服务独立抽象开,让各自功用最大化,这种分布式才有意义,对于小体量的采集,分布式就完全没必要使用了。
因为我爬虫的目的都是给自己站点提供更新内容,所以,内容的伪原创特别重要,如果能把文章伪原创做的与各引擎检索库存在文章相异度极大,同时,针对于站点各聚合页面有很好的组织方式,便于引诱蜘蛛提高收录量、提高站点整体权重,那针对一些关键词的排名就相对容易多了,所以现在一般的瓶颈都不在爬虫抓取的部分,而是出现在如何把抓取到的数据如何更好的与后面清洗、加工流程整合起来。比如说我抓取10000
个站点,怎么把这10000
个站点采集到的各自专题方向的数据都聚合到一个地方,让后面清洗、加工工种人员更高效介入?此时你可能嗅到面临到的问题了,比如说如果前面采集到的数据全都往mysql
或mongodb
数据库写,数量量小的时候还可以扛一扛,但是数据量一大,亚根就写不动了,加数据服务器又没多大必要,毕竟这种大负载场景不是遍布分分秒秒的,就算加了,加工时候取数点太多,多结点数据再整合又成新问题了,所以怎么来分散这种压力,同时又能把各自主题内容合理划分,让后面清洗、加工更高效聚集在自己业务上就成了当务之急需解决的问题了,那该怎么办呢?
所以kafka
的引入就成了一种势在必行了,先来看下kafka
有哪些特征?
- 先进先出
- 多组多次消费,可以指定很多的
topic
,比如说做报警的,做写库的,做统计的,做深度学习的,彼此独立,互不耽误,高效使用数据源 - 容量大
- 比任何数据库都更能扛住高并发,1s百万次并发,谁可一战?
- 数据持久化,比如可以保留7天
- 可以设定从头开始还是从最新开始
- 可以根据写入时间定位某个特定的offset,跟
redis
的zset
数据结构很像,可以根据时间戳范围进行范围数据查找,这个也类似
整体看下来,是不是有似曾相识的感觉,如我最后一条的举例,其实类似redis
,但是redis
队列具备的好处kafka
也有,但redis
一条数据正在消费,没来得及消费完成,如果这时候服务挂了,消费的那条数据没有完成全链路流程,想重来一次?“对不起,这个真没有”。但是kafka
它支持数据的持久化,支持多组多次消费,你这种需求它完全支持。当然也不是说redis
不能具备这个功能,但是你需要扩展、需要外部引入,这个我们就不深究了。
同时,常写爬虫,你可能意识到用scrapy
现在把有些工作揉到一块了。比如说与爬取站点的反爬取机制斗智斗勇、用xpath
或selenium
解析页面,这正常就是爬虫部分唯一需要关注的事。但是现在很多清洗、加工工作也在由爬虫部分来完成,这样不方便彼此分工、同步开发。这个时候kafka
先拦一层,后续清洗工组部分把原生抓进来的粗数据进一步加工,再写进一个kafka
,后续加工工组部分再进一步加工再入库,这样就彼此不耽误,可以高效同步开发了。
当然,好多MQ工具都可以作这层kafka
担任的角色,但是kafka
的高并发性能高得惊人,普通机器每秒十万,性能机每秒百万也不在话下,这种性能无出其右,也是我选择kafka
作为MQ
角色的重要原因。
好了,今天这篇有关经验分享的文章就到这里了,没有涉及具体的代码,只想给出一些思路和宏观建议,太细节的东西如果你不实操,你永远无法领略一个个问题被解决之后的欣喜。同时,我也在着手录制一套有关爬虫的高阶实战课,全面新颖,不居一格,深度剖析,妙笔生花,相信会给你一次与众不同的学习体验和游弋知识海洋的饕餮盛宴,感谢阅读。