page contents

爬虫 | 做一个简单的动态代理池

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

使用代理服务器一直是爬虫防BAN最有效的手段,但网上的免费代理往往质量很低,大部分代理完全不能使用,剩下能用的代理很多也只有几分钟的寿命,没法直接用到爬虫项目中。

下面简单记录一下我用scrapy+redis实现动态代理池的过程。


对“动态代理池” 的需求

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

2. 始终保持一个相对稳定的代理数量

3. 始终保持池内代理的高可靠率(希望90%的代理都能用)

4. 尽可能减少对爬虫项目代码的更改


参考过的项目

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

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)


流程图:

attachments-2019-12-v9JgO0cJ5e06b2e7be964.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时候的截图:

attachments-2019-12-Aq8Cj0kE5e06b351e3f4f.png

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-06-r3rHLJxN62a43b4669873.jpeg

  • 发表于 2019-12-28 09:44
  • 阅读 ( 611 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1312 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章