基于redis实现高并发下的IP代理池可靠更换
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了基于redis实现高并发下的IP代理池可靠更换,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含2732字,纯文字阅读大概需要4分钟。
内容图文
![基于redis实现高并发下的IP代理池可靠更换](/upload/InfoBanner/zyjiaocheng/1181/f51ac0003bac40fe81b6e267775f4c62.jpg)
业务需求
现需对某国外图片网站进行大量爬取,为提高效率使用多进程,对多个子目录下的图片同时爬取。由于网站对单IP的下载量有限额,需要在额度耗尽时自动从代理池里更换新代理。IP的可用额度无法在本地计算或实时获取,只有在耗尽时才能从目标网站得到异常通知。
业务分析
虽然是单机并发,但所面对的问题其实属于分布式领域。由于网站并未对访问频率作出限制,所以只需考虑IP的下载总量即可,可让所有进程都走同一个代理IP;又因为代理会随时变更,所以应该在每次下载请求时实时获取,这里使用redis维护代理地址比较合适。爬虫用Python编写,所以用Python的redis客户端。
业务实现
假设代理池中有5个地址,192.168.1.1:1080~192.168.1.1:1084,首先我们随机选一个,比如192.168.1.1:1080,作为初始代理。在redis中新建元素proxy和proxylist
127.0.0.1:6379> set proxy 192.168.1.1:1080127.0.0.1:6379> RPUSH proxylist 192.168.1.1:1080192.168.1.1:1081192.168.1.1:1082192.168.1.1:1083192.168.1.1:1084
Python代码
# 版本1 def getproxy(): proxy = rconn.get(‘proxy‘).decode() return {‘http‘:proxy, ‘https‘:proxy} def changeproxy(): proxies = rconn.lrange(‘proxylist‘, 0, 5) current_proxy = getproxy().get(‘http‘) rconn.set(current_proxy, ‘cooling‘, ex=100000) #建立一个key为当前代理IP的键,表示此IP已经进入冷却,并用ex设定冷却时间for proxy in proxies: ifnot rconn.get(proxy): #排除在冷却时间内的IP res=rconn.set(‘proxy‘,proxy) return res return None
但此法不久后在实践中遇到了一个问题:某次一个干净的代理被设置成冷却中了;后发现,因为任务是并发的,当1号请求返回异常并更改了代理后,使用了前代理的2号请求才返回出异常,于是又触发了一次更换请求,相当于短时间内连续更换了两次代理,造成了资源的浪费。
# 版本2 def getproxy(): proxy = rconn.get(‘proxy‘).decode() return {‘http‘:proxy, ‘https‘:proxy} def changeproxy(): proxies = rconn.lrange(‘proxylist‘, 0, 5) current_proxy = getproxy().get(‘http‘) ‘‘‘ 检测是否有保护标志 ‘‘‘if rconn.get(‘protect_time‘): return True rconn.set(current_proxy, ‘cooling‘, ex=100000) #建立一个key为当前代理IP的键,表示此IP已经进入冷却,并用ex设定冷却时间for proxy in proxies: ifnot rconn.get(proxy): #排除在冷却时间内的IP res=rconn.set(‘proxy‘,proxy) ‘‘‘ 在成功更换代理后,放置一个有效期为30s的保护标志,该标志存在期间禁止代理更换。这个有效期理论上最短要设置为一次请求报文的往返时间 ‘‘‘ rconn.set(‘protect_time‘, ‘yes‘, ex=30) return res return None
这种方法能避免有效代理被跳过,但如果代理池里不小心混入了脏代理,且被更换到了,那在这30s的保护时间内,脏代理也会被“保护”,即使时间不长,我们也要想办法避免。笔者想了很久,有没有在客户端不传任何参数的情况下解决这一点,包括错误计数器,进程标识,保护时间削减等等,但最后发现,唯一可靠的还是客户端传当前代理给代理服务器。
# 版本3(最终) def getproxy(): proxy = rconn.get(‘proxy‘).decode() return {‘http‘:proxy, ‘https‘:proxy} def changeproxy(local_proxy): # local_proxy是客户端发起请求并被返回异常时所用的代理IP proxies = rconn.lrange(‘proxylist‘, 0, 5) current_proxy = getproxy().get(‘http‘) if local_proxy!=current_proxy: return True else: rconn.set(current_proxy, ‘cooling‘, ex=100000) #建立一个key为当前代理IP的键,表示此IP已经进入冷却,并用ex设定冷却时间for proxy in proxies: ifnot rconn.get(proxy): #排除在冷却时间内的IP res=rconn.set(‘proxy‘,proxy) return res return None
可以看到,最终版本不仅更加简洁,也解决了上述提到的问题。
------
如果要细究,最终版也是有漏洞的,因为整个更换代理的操作并不具备原子性,依旧可能造成代理被跳过(虽然概率极其微小)。而redis又没有真正的事务,所以最好为changeproxy()再加一把锁,或者对最终版做一处微小的修改:
else : flag = rconn.set(current_proxy, ‘cooling‘, ex=100000, nx=True) #nx参数令存在该键时就不建立,并返回falseifnot flag: return True for proxy in proxies: ifnot rconn.get(proxy): res=rconn.set(‘proxy‘,proxy) return res
原文:https://www.cnblogs.com/qjfoidnh/p/12152945.html
内容总结
以上是互联网集市为您收集整理的基于redis实现高并发下的IP代理池可靠更换全部内容,希望文章能够帮你解决基于redis实现高并发下的IP代理池可靠更换所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。