问题与分析
问题
最近有个朋友问我能不能帮我写个脚本投投票啊,我看今天有人一下子多了4万的投票,这肯定是作弊啊,你能不能帮帮我?我就很好奇这到底是什么原因。
分析
于是我打开他给我的链接一看,非常简单的投票系统,就是发一个个的Get请求就可以了。
我看也挺简单,第一反应就直接写个python脚本去轮询。当然先用Postman做了最简单的测试,手动发了很多Get request,但是不一会儿问题来了。
需要输入验证码,短时间内过高的请求会被目标系统的反爬虫系统识别,IP会被锁定。目前有两个问题需要解决:
- 如果需要输入验证码,该如何去解析呢?
- 为何IP会被封呢?
针对第一个问题,首先想到的是拿到验证码的图片,然后通过开源软件库将验证码解析出来,经过分析,这个验证码是通过页面点击控件,JS代码触发Get请求生成的,这意味着很难拿到图片,即使可以,那也要通过UI的自动化框架去获取,通过点击控件,然后解析页面元素,很麻烦,而且稳定性不高。
不过既然单个IP会被封,那么通过大量的代理IP去投票是不是就可以了?遇到验证码就换IP,这样是不是就可以规避这个问题了?不错,这是一个很好的解决办法。有了具体的方案之后,开始代码的实施。
实现
获取IP资源
去网上花了5块钱买了两个IP资源,有效期一天,通过其API接口可以拿到有效的IP和端口号。对于IP资源,不同平台价格、时效、质量不同,网络上也有不少的IP和端口供人使用,经过尝试后都不能用,端口资源被限制。
通过请求暴露的API接口,IP服务商会返回相关的IP信息,下面是返回是带有服务器IP和端口的响应数据:
1 2 3 4 5 6 7 8 9 10 11 12
| { "code":200, "success":true, "data":[ { "ip":"115.213.103.48", "port":3000, "during":2 } ], "msg":"操作成功" }
|
Coding
每次请求,获取IP资源也是新的,所以只要在投票有问题之后,更换IP即可。 只要拿到的结果是成功的,就把IP和端口号解析出来。如果中间出现了什么Exception,那就休息一会,重新更换IP,主要是IP服务商的响应不及时,会有一些错误信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def get_http_server(self, url):
res = requests.get(url, timeout=4) res.encoding = 'gb2312' try: rep_dict = json.loads(res.content.decode()) if rep_dict['success'] and rep_dict['code'] == 200: ip = rep_dict['data'][0]['ip'] port = rep_dict['data'][0]['port'] return f"{ip}:{port}" return None except: print("换IP,休息5秒") time.sleep(5)
|
下面的代码中我隐去了API信息,但是不影响逻辑。主要的逻辑是通过代理IP不断地去发送请求,将票数和排名打印出来,如果排名到了第三名,那么投票的票数就够了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| def vote(self, url_get_ip): url = 'vote request url' s = requests.Session() ip_port = self.getHttpServer(url_get_ip) if ip_port: while True: try: time.sleep(2) proxies = { "http": "http://{}".format(ip_port), "https": "https://{}".format(ip_port) } res = s.get(url, headers=self.headers, proxies=proxies, timeout=10) res.encoding = 'gb2312' rep_dict = json.loads(res.content.decode()) print(f"票数:{rep_dict['data']['count']} ===> 排名: {rep_dict['data']['rank']}") if rep_dict['data']['rank'] == 3: self.not_enough = False break if rep_dict['data']['msg'] == 'need_captcha': print('验证码校验,准备换IP') return False except: print('投票有点小问题,pass') return False else: return False
|
到目前为止,已经刷到了前5名。这篇文章的目的不是说做什么偏门作弊的东西,只是个人的一种技术尝试,我觉很有意思,就记下来了。
源码
仅供学习和参考:
https://github.com/SouthernYard/vote