给自己的爬虫做一个简单的动态代理池

使用代理服务器一直是爬虫防BAN最有效的手段,但网上的免费代理往往质量很低,大部分代理完全不能使用,剩下能用的代理很多也只有几分钟的寿命,没法直接用到爬虫项目中。
下面简单记录一下我用scrapy+redis实现动态代理池的过程。

我对“动态代理池” 的需求

我的爬虫项目需要7*24小时监控若干个页面,考虑了一下希望代理池能满足下面几个要求:

  • 始终保持一个相对稳定的代理数量
  • 始终保持池内代理的高可靠率(希望90%的代理都能用)
  • 尽可能减少对爬虫项目代码的更改

参考过的项目

找过一些现成的轮子,但发现或多或少都不太符合我的需求

kohn/HttpProxyMiddleware
一个“被动”选择代理的方式。在scrapy middleware中加入大量代码,让爬虫在代理失效后再去代理网站找代理,会有下面几个问题:

  • 大多数情况下始终使用一个代理去访问,造成流量集中在一个IP上,可能导致代理IP被BAN。
  • 切换代理的过程比较耗时,导致爬虫性能下降比较厉害
  • 所有逻辑都嵌套在了爬虫项目里面,有兼容性和可移植性方面的问题(有多个爬虫的话每个都要改一遍..)
  • 基于python2写的,我的项目在python3上..

jhao104/proxy_pool
这个几乎和我想要的一样,但还是因为下面几点原因没有使用:

  • 基于SSDB,这边没有现成的SSDB,因为已经在用Redis了不想专门去装一个
  • 只管定时收集代理,在收集过程中做一次验证。但事实上收集到的代理即便通过了验证,也可能活不过10分钟,时间一长代理池内就会有很多过期的代理,需要在爬虫代码中处理这些过期代理(爬虫代码调用delete删除)。

设计思路和代码

看了那么多方案,我就在想:能不能让代理池具有自我检查自我修复的功能?这样我们的爬虫就只需随机拿一个代理用就可以了,就算恰巧拿到一个失效代理,只要做一次retry,代理池的修复机制可以保证retry的时候失效的代理已经被移走了,不会再次取到。

简单规划一下:

  • 我们需要一个自检程序,它每10S跑一次,保证代理池内所有代理至少在10S内有效
  • 我们需要一个代理获取的程序,当代理池内代理数量过低时(阈值定为<5),访问免费代理网站补充新代理
  • 我们需要一个调度程序,用来监控代理池数量并调用上面两个程序,维持代理池平衡。
  • 作为私有代理池就维护在redis了(SET),让爬虫直接从redis里取代理。

对应的代码就有这么三个部分:

  • 一个scrapy爬虫去爬代理网站,获取免费代理,验证后入库 (proxy_fetch)
  • 一个scrapy爬虫把代理池内的代理全部验证一遍,若验证失败就从代理池内删除 (proxy_check)
  • 一个调度程序用于管理上面两个爬虫 (start.py)

强行画个流程图:

hq-proxies.png

爬虫部分全部用scrapy写了,其实没必要,用requests就够了..但做的时候刚学scrapy,就顺便练练手了..
start.py的调度方式比较粗暴,直接起两个线程,在线程内用os.system调用scrapy爬虫(毕竟轻量级代理池嘛..好吧其实就是我懒图个方便..)

另外还有几个调度策略需要说一下:

  • 每次调用proxy_fetch后会自动设定一个“保护时间”10分钟,在保护期内除非代理池只剩一个代理了,否则不会触发proxy_fetch,避免频繁调用。
  • 加入了一个“刷新时间”24小时,保证每24小时内至少执行proxy_fetch一次
  • 验证程序设定timeout为5秒,5秒内没访问到测试页面就认为验证失败

其他细节可以看源码:arthurmmm/hq-proxies

部署和使用

需要先改一下配置文件hq-proxies.yml,把Redis的地址密码之类的填上,改完后放到/etc/hq-proxies.yml下。
在配置文件中也可以调整相应的阈值和免费代理源和测试页面。
测试页面需要频繁访问,为了节省流量我在某云存储上丢了个helloworld的文本当测试页面了,云存储有流量限制建议大家换掉。。验证方式很粗暴,比较一下网页开头字符串。。

另外写了个Dockerfile可以直接部署到Docker上(python3用的是Daocloud的镜像),跑容器的时候记得把hq-proxies.yml映射到容器/etc/hq-proxies.yml下。
手工部署的话跑pip install -r requirements.txt安装依赖包

在scrapy中使用代理池的只需要添加一个middleware,每次爬取时从redis SET里用srandmember随机获取一个代理使用,代理失效和一般的请求超时一样retry,代理池的自检特性保证了我们retry时候再次拿到失效代理的概率很低。middleware代码示例:

class DynamicProxyMiddleware(object):
    def process_request(self, request, spider):
        redis_db = StrictRedis(
            host=LOCAL_CONFIG['REDIS_HOST'], 
            port=LOCAL_CONFIG['REDIS_PORT'], 
            password=LOCAL_CONFIG['REDIS_PASSWORD'],
            db=LOCAL_CONFIG['REDIS_DB']
        ) 
        proxy = redis_db.sismember(PROXY_SET, proxy):
        logger.debug('使用代理[%s]访问[%s]' % (proxy, request.url))
        request.meta['proxy'] = proxy

在其他爬虫框架下使用也是类似的。

最后放一张萌萌哒日志菌触发proxy_fetch时候的截图:

Paste_Image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1 前言 作为一名合格的数据分析师,其完整的技术知识体系必须贯穿数据获取、数据存储、数据提取、数据分析、数据挖掘、...
    whenif阅读 18,107评论 45 523
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,991评论 19 139
  • 风险分为不同的层次面,我们现阶段可分为规避,降低,自留。 工作中不可避免的人员去留,相对表现通过观察与沟通提前做好...
    杨平的阅读 277评论 0 0
  • 企业所有的制度出台一定有它独特的缘由,企业各项规章制度是企业的内部法规, 是企业日常管理行为的重要依据,它可...
    YY的世界阅读 555评论 2 1
  • 今天参加了一个世界诗歌日的朗诵活动。诗歌配春雨,不错。 分享一下我的自选诗歌。 推荐一个APP,为你读诗。
    寒晗Tylor阅读 227评论 0 3