问题与分析

问题

最近有个朋友问我能不能帮我写个脚本投投票啊,我看今天有人一下子多了4万的投票,这肯定是作弊啊,你能不能帮帮我?我就很好奇这到底是什么原因。

分析

于是我打开他给我的链接一看,非常简单的投票系统,就是发一个个的Get请求就可以了。

我看也挺简单,第一反应就直接写个python脚本去轮询。当然先用Postman做了最简单的测试,手动发了很多Get request,但是不一会儿问题来了。

需要输入验证码,短时间内过高的请求会被目标系统的反爬虫系统识别,IP会被锁定。目前有两个问题需要解决:

  1. 如果需要输入验证码,该如何去解析呢?
  2. 为何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