针对特定网站scrapy爬虫的性能优化
在使用scrapy爬虫做性能优化时,一定要根据不同网站的特点来进行优化,不要使用一种固定的模式去爬取一个网站,这个是真理,以下是对58同城的爬取优化策略:
一、先来分析一下影响scrapy性能的settings设置(部分常用设置):
1,DOWNLOAD_TIMEOUT,下载超时,默认180S,若超时则会被retry中间件进行处理,重新加入请求队列
2019-04-18 20:23:18 [scrapy.downloadermiddlewares.retry] DEBUG: Retrying <GET https://bj.58.com/ershoufang/37767297466392x.shtml> (failed 1 times): User timeout caused connection failure: Getting https://bj.58.com/ershoufang/37767297466392x.shtml took longer than 180.0 seconds..
一般来说访问一个网站都是以ms作为单位的,180S确实有些太长了,而且,由于默认设置了最大并发数为16,导致这些request没有下载到东西还一直占据着并发数,在我的日志文件中就能大量看到retry日志
2.DOWNLOAD_DELAY,该选项默认为0,即在下载是并发执行,若设置为x,则每隔 0.5*random~1.5*random 秒下载下一个url,影响当然很大,很多时候还是建议给个正值,避免直接把服务器弄炸了,而且在IP准备不充分情况下,也有利于爬虫的持续运行,对双方都有好处
3.CONCURRENT_REQUESTS ,默认为16,下载器下载的并发数,建议调高,根据scrapy发挥到cpu核心性能80~90%取适应值,若服务器有反爬措施,自身准备IP又不充足情况下,建议调低;
scrapy 是基于 twisted 的异步 IO,只用到单线程,若考虑部署一台服务器专用于爬虫,请选择更高的单核性能,关于scrapy单线程的证明:
设置CONCURRENT_REQUESTS=100,让scrapy爬取一个超过100个链接的页面,检测下载前后线程数量,windows上直接在任务管理器就能看到了,没有放对比图,就我的观测来看,爬取前后线程数量是没有增加的
4.CONCURRENT_REQUESTS_PER_IP ,下载器下载时针对每个IP的并发数,默认是0,这里0指不限制的意思,取值视IP充足情况和CONCURRENT_REQUESTS决定,若DOWNLOAD_DELAY非零,搭配使用可以实现针对IP做并发下载的download_delay,如CONCURRENT_REQUESTS=16,CONCURRENT_REQUESTS_PER_IP =3,DOWNLOAD_DELAY = 2
当前下载器正在并发下载16个Request,ip:https://201.182.248.10:8080被随机分配到4个Request中,那么有一个使用这个ip代理Request从下载队列中出局,并且在2秒钟内下一个Request携带的代理仍是这个ip时,仍然会被出局,这还是我的一个猜想,有空做一个合适的模型证明一下。
二、优化方案
在执行以下每个策略之前都会清除掉上一次保存在数据库中的item,并且重新获取一定数量的优秀代理供使用,这次优化评判标准是爬取到50条ITEM,比较哪种设置的爬虫用到的时间更短,
初始设置:
CLOSESPIDER_ITEMCOUNT = 50
DOWNLOAD_DELAY=0.5
CONCURRENT_REQUESTS = 16
结果:
用时:7分21秒
策略1:比原设置更少的DOWNLOAD_TIMEOUT
设置:
CLOSESPIDER_ITEMCOUNT = 50
DOWNLOAD_DELAY=0.5
CONCURRENT_REQUESTS = 16
DOWNLOAD_TIMEOUT = 15
结果:
用时:2分20秒
结果分析:更少的DOWNLOAD_TIMEOUT换来更多的重试次数,但是由于下载超时大大减少,使scrapy性能大大提升。这个结果同时提供一种后续继续优化思路:逐渐减小DOWNLOAD_TIMEOUT的值,使得爬虫用时减少而重试次数增加的较慢,那么这应该就是一个合适的DOWNLOAD_TIMEOUT取值
策略2:比策略1更少的DOWNLOAD_DELAY,直接注释,使用默认0延迟
设置:
CLOSESPIDER_ITEMCOUNT = 50
#DOWNLOAD_DELAY=0.5
CONCURRENT_REQUESTS = 16
DOWNLOAD_TIMEOUT = 15
结果:
用时:2分19秒
结果分析:似乎和策略1结果差别不大,应该是策略1中设置的TIMEOUT_DELAY较小,在我把策略1换更大的TIMEOUT_DELAY之后,爬取用了10多分钟,后来没等,直接关闭了,就没上结果图片了。
策略3:提供比策略2更大的CONCURRENT_REQUESTS
设置:
CLOSESPIDER_ITEMCOUNT = 50
#DOWNLOAD_DELAY=0.5
CONCURRENT_REQUESTS = 50
DOWNLOAD_TIMEOUT = 15
结果:
用时:4分25秒
结果分析:这和前面的scrapy性能分析完全不符,并发数越大应该爬取速度更快才对,现在用了原来近2倍时间。来分析一下原因,可以看到,对比策略2的日志图片,策略3请求数量明显增大,223 -- >318,重试次数也明显增大,29->101,并且,此项目中使用的ip都是从本地文件中的ip随机获得的,且并没有在获取一个ip之后删除掉这个ip,可以这样分析:并发增多 -- >携带同一ip的并发请求也增多 -- > 被ban的ip也会增多 --> redirect到firewall的请求增多 -- > Request数量增多 --> retry数量增多,这样很好解释了日志中的结果。这个结果同时提供我们启示:要想增大并发数以提高爬虫效率,必须保证单次请求IP更高有效性,为了解决这个问题,后面我会借鉴git大神写的Flask线程池,提高单次请求成功率。
策略4:CONCURRENT_REQUESTS_PER_IP,对ip做并发限制
CLOSESPIDER_ITEMCOUNT = 50
#DOWNLOAD_DELAY=0.5
CONCURRENT_REQUESTS = 16
CONCURRENT_REQUESTS_PER_IP = 1
DOWNLOAD_TIMEOUT = 15
结果:
用时:35分16秒
结果分析:scrapy对使用相同ip的并发请求的处理机制对这个结果有一定影响,更加根本原因还是和策略3一样,源于ip有效性较差,下篇我将会使用flask搭建线程池以比较这四种策略的性能差异
总结:
从本例看,策略1应该就是最好的方案了,在允许少量下载延迟的情况下,也不需要大并发,照样提供更好的性能,这也正如开头所说的,对scrapy做性能优化还是要针对具体项目,具体条件。
posted on 2019-04-19 18:20 Tarantino 阅读(2339) 评论(0) 编辑 收藏 举报