你还在苦苦拉票吗?
前言
- 剖析投票原理
- 处理思路
- 具体实战
- 主要流程
- 具体细节 python
- 代码实例 python
- 具体细节 java
- 代码实现 java
- 总结
前言
现在生活中离不开各类的比赛,然而,各个比赛离不开投票,我们会清一色有时候找到我们的朋友在朋友圈发 — 帮宝贝投一票,帮某某老师,学生投一票。又或许你自己参加比赛,你在为你自己拉票。
剖析投票原理
作为一名程序员,你是否想为自己的生活开一点 G 呢?熟悉网络请求的我们,应该从问题根源分析问题。对于一个投票的网站。大致分为两类:
- 登录类: 这类网站是确实不太好操作,一般是每个账户每天能够刷若干票。因为账户的资源是有限的,我们很难通过获取大量的账户资源为我们服务。况且,一般的登录稍微大厂或者技术高点其中的 js 加密会比较复杂,对于普通人 js 水平不高很难行的通这条路。比如常见需要微信登录,qq 登陆的网站投票,就很难开挂。
- 非登录类: 并不是所有网站都有腾讯的登录授权的,有很多他们自己的官网他们自己就是一个体系。这类网站普通人或许也感觉不到差异:投几票之后也不能投。然后纷纷找朋友帮忙投。剖析这类网站,既然没有登录机制,那么它就是根据 ip 机制进行鉴定。因为正常你的公网 ip 相对来说是稳定。所以正常一个用户只能投固定的几票。或许高级一点他会和浏览器信息结合鉴定,但这种还是比较少的。
处理思路
既然原理已经剖析完成,那么剩下的就是设计程序的问题了,对于一个点击投票的事件,它的实质就是一次 http (post) 请求,然后后台对数据进行更改。那么我们就可以对这个操作流程进行抓包,分析这个请求是那种类型,需要那些参数。然后根据这个请求模拟写出请求。
然而最重要的就是 ip 代理,你要用代理的 ip 去访问那个接口,让对方以为是你代理的那个 ip 再对他访问,所以你需要维护一个代理 ip 池。对于代理 ip 池,并不是什么高大上的东西,准确的来说就是一个集合中包含一些可用的 ip,能够供我使用。市面上也有很多出售代理 ip,也不贵。我用的是蘑菇代理。
具体实战
主要流程
碰巧,最近参加的一个比赛就有拉票环节,如果人为手动拉票的话效率地下,并且你肯定也不会愿意天天去舔人家求情。那就自己分析一波!
- 首先,打开你在的网站(有的手机端,电脑端好抓包可调),谷歌或者其他浏览器 F12 抓包,点击 network,xhr 准备(肯定是 ajax 请求不用想)。
- 分析这个请求的重要参数.(header)
找到 url 和几个参数,就可以准备程序了。模拟请求了
具体细节 python
因为这是多次请求,所以要考虑性能的问题和效率问题。不能让异常漫天飞,中断,ip 白白浪费,或者苦苦等待吧。 对于代理 ip,各家卖的虽然有些差异但是大体相同。大致均为卖数量,然后每个 ip 从开始被用后能够维持几分钟的使用。并且有的 ip 是不能用的,有的是高延迟的,这些在写程序的时候都要过滤掉。这里面就要考虑下这个程序额设计。
- 多线程: python 虽然多线程有个全局锁大大的影响效率。但是对于 io 请求型多线程还是能有一定的提速的。因为 io 有大量的线程等待。多线程的模块大致为定义一个线程类,定义初始方法和 run 函数。然后在外面定义几个线程,让他们跑任务。
- ip 处理和资源处理 正常的 ip 代理是一群 ip 随机抽取其中作为代理 ip,进行爬取任务,然后 ip 失效从 ip 池中删除。而 url 的链接一般放到线程安全的全局容器中一个个抛出。ip 放到 list 或者 redis 中进行维护,做好 try catch 异常即可。但是这个刷票只有一个 url。并且一个 ip 只能用有限次数。所以换个思路,url 不需要容器维护。而 ip 用队列维护最好,并且 python 的队列是线程安全的。所以整个程序的架构也就很清晰了。只需要用个 queue 解析 ip 获取的格式进行相应储存。然后被消费,当少于一定个数时,请求 api 获取 ip 进行填充。
- 在预处理方面,以前介绍过另一个蘑菇代理使用和 ip 池类似的问题,可以预先参考。
代码实例 python
代码语言:javascript复制import requests
import random
import time
import threading
from queue import Queue
def loadip():##从代理ip中获取ip 一次若干扩充到queue中
url2 = 'http://piping.mogumiao.com/proxy/api/get_ip_al?appKey=f16367295e284173ae450f&count=20&expiryDate=0&format=1&newLine=2'
req = requests.get(url2)
date = req.json()
if(date['code'])!='3001':
ipdate2 = date['msg']
for va in ipdate2:
que.put(va)
class downspider(threading.Thread):##线程类
def __init__(self, threadname, que):
threading.Thread.__init__(self)
self.threadname = threadname
self.que = que
def run(self):
print('start thread' self.threadname)
while True:
try:
toupiaospider(que,self.threadname)##投票函数
except Exception as e:
print(e,'888')
break
def getproxies():#获取ip 拼接成需要的代理格式
b=que.get()
d = '%s:%s' % (b['ip'], b['port'])
global proxies
proxies['http'] = d
return proxies
def toupiaospider(que,threadname):
if (que.qsize() < 15): # 拓展ip池
loadip()
proxies2=getproxies()
for i in range(0,5):
try:
#formData['times']=i
req = requests.post(url, headers=header, data=formData, proxies=proxies2, timeout=1.5)
res = req.json()
if res['res']==2001 or req.status_code!=200:
continue
print(threadname,proxies2['http'],res,que.qsize())
except Exception as e:
print('errror',e)
if __name__ == '__main__':
proxies = {'http': ''}
stadus = 0
que = Queue()
threads=[]#线程
url='http://yunxin.163.com/api/vote/update'
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
formData = {
'Referer':'http://yunxin.163.com/promotion/minichallenge/gallery?from=groupmessage&isappinstalled=0',
'id':'17',
'times':'1',
'activity':'minichallenge1'
}
proxies = {'http': '182.247.92.99:21136',
}
loadip()
time.sleep(5)
##线程数组 ->启动 ——>等待join
threadList = ['thread-1','thread-2','thread-3','thread-4','thread-4','thread-5']
for j in threadList:
thread = downspider(j, que)
thread.start()
threads.append(thread) for t in threads:
t.join()
结果
具体细节 java
在 java 中比较棘手的就是 java 自身对 json 和 http 请求的处理不太方便,需要借助第三方 jar,并且一些操作稍显的繁琐。
首先 java 要弄清几点:
- 代理方式: java 主要有两种代理方式,一种是 jdk 全局代理,另一种是 net 包下的 proxy 代理。对于多线程程序并且 ip 只能用一次的当然是用 net 的 proxy 代理。
- 解析 json 通过 api 获取 ip,格式固定的,需要借助 fastjson 解析 json 串获取需要的信息。
- 线程安全问题。你可以用线程安全的 blockqueue,当然其实你可以在操作队列的方法加上 synchronized 关键字也可以。你可以定义固定的线程每个线程任务多个。也可以用线程池定义多个线程类,每个线程完成一个任务。
- 网络请求虽然 urlconnection 可以实现,但是太繁琐,远比 jsoup 复杂。所以这里使用 jsoup。
针对上面的问题。写了个 demo 测试进行预备,对于获取 ip 的 api,大致这种格式
首先你要下载 fastjson 和 jsoup 的 jar 包。或者加入 maven 依赖。(可在 maven 官网下 jar 包)
代码语言:javascript复制 <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
然后写个 demo 跑一下