如今生活中離不開各種的比賽,然而,各個比賽離不開投票,咱們會清一色有時候找到咱們的朋友在朋友圈發—幫寶貝投一票,幫某某老師,學生投一票。又或許你本身參加比賽,你在爲你本身拉票。java
做爲一名程序員,你是否想爲本身的生活開一點G呢?熟悉網絡請求的咱們,應該從問題根源分析問題。對於一個投票的網站。大體分爲兩類:node
既然原理已經剖析完成,那麼剩下的就是設計程序的問題了,對於一個點擊投票的事件,它的實質就是一次http(post)請求,而後後臺對數據進行更改。那麼咱們就能夠對這個操做流程進行抓包,分析這個請求是那種類型,須要那些參數。而後根據這個請求模擬寫出請求。python
然而最重要的就是ip代理,你要用代理的ip去訪問那個接口,讓對方覺得是你代理的那個ip再對他訪問,因此你須要==維護一個代理ip池==。對於代理ip池,並非什麼高大上的東西,準確的來講就是一個集合中包含一些可用的ip,可以供我使用。市面上也有不少出售代理ip,也不貴。我用的是蘑菇代理。git
碰巧,最近參加的一個比賽就有拉票環節,若是人爲手動拉票的話效率地下,而且你確定也不會願意每天去舔人家求情。那就本身分析一波!程序員
首先,打開你在的網站(有的手機端,電腦端好抓包可調),谷歌或者其餘瀏覽器F12抓包,點擊network,xhr準備(確定是ajax請求不用想)。github
分析這個請求的重要參數.(header)ajax
找到url和幾個參數,就能夠準備程序了。模擬請求了由於這是屢次請求,因此要考慮性能的問題和效率問題。不能讓異常漫天飛,中斷,ip白白浪費,或者苦苦等待吧。 對於代理ip,各家賣的雖然有些差別可是大致相同。大體均爲賣數量,而後每一個ip從開始被用後可以維持幾分鐘的使用。而且有的ip是不能用的,有的是高延遲的,這些在寫程序的時候都要過濾掉。這裏面就要考慮下這個程序額設計。redis
多線程: python雖然多線程有個全局鎖大大的影響效率。可是對於io請求型多線程仍是能有必定的提速的。由於io有大量的線程等待。多線程的模塊大體爲定義一個線程類,定義初始方法和run函數。而後在外面定義幾個線程,讓他們跑任務。json
ip處理和資源處理 正常的ip代理是一羣ip隨機抽取其中做爲代理ip,進行爬取任務,而後ip失效從ip池中刪除。而url的連接通常放到線程安全的全局容器中一個個拋出。ip放到list或者redis中進行維護,作好try catch異常便可。可是這個刷票只有一個url。而且一個ip只能用有限次數。因此換個思路,url不須要容器維護。而ip用隊列維護最好,而且python的隊列是線程安全的。因此整個程序的架構也就很清晰了。只須要用個queue解析ip獲取的格式進行相應儲存。而後被消費,當少於必定個數時,請求api獲取ip進行填充。api
在預處理方面,之前介紹過另外一個蘑菇代理使用和ip池相似的問題,能夠預先參考。
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自身對json和http請求的處理不太方便,須要藉助第三方jar,而且一些操做稍顯的繁瑣。
首先java要弄清幾點:
針對上面的問題。寫了個demo測試進行預備,對於獲取ip的api,大體這種格式
首先你要下載fastjson和jsoup的jar包。或者加入maven依賴。(可在maven官網下jar包)<!-- 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跑一下
package com.bigsai;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class test2 {
static int va=1;
public static void main(String[] args) {
String ti="{\"code\":\"0\",\"msg\":[{\"port\":\"40034\",\"ip\":\"114.237.64.247\"},{\"port\":\"33257\",\"ip\":\"223.240.210.250\"},{\"port\":\"39618\",\"ip\":\"113.101.255.11\"},{\"port\":\"43151\",\"ip\":\"183.135.106.62\"},{\"port\":\"41795\",\"ip\":\"182.108.44.227\"}]}";
JSONObject jsonObject= JSON.parseObject(ti);
String code=(String) jsonObject.get("code");
JSONArray jsonArray=jsonObject.getJSONArray("msg");
for(Object te:jsonArray)
{
JSONObject team=(JSONObject) te;
String ip=team.getString("ip");
int port=team.getInteger("port");
System.out.println(team+" "+ip+" "+port);
}
ExecutorService ex= Executors.newFixedThreadPool(10);
for(int i=0;i<200;i++)
{
threadtest threadtest=new threadtest();
ex.execute(threadtest);
}
ex.shutdown();
}
static synchronized void addva()//去掉註釋試試 {
va++;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+va);
}
static class threadtest implements Runnable{
@Override
public void run() {
addva();
}
}
}
複製代碼
觀察結果。打印,這些邊角問題你就明白了。就能夠設計java程序了。
package com.bigsai;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class wangyi {
//改爲你本身的token已經被我改了
static String ipurl="http://piping.mogumiao.com/proxy/api/get_ip_al?appKey=f16367295e284173ae3&count=20&expiryDate=0&format=1&newLine=2";
/** * java的多線程和python略有不一樣,但也能夠改爲類似的 * py是5個線程每一個線程死循環任務,而java用線程池每一個線程一個任務,只不過new了不少對象 * @param args */
static Queue<proxynode> q1;
public static void main(String[] args) {
q1=new ArrayDeque();//隊列存放結構體 ip和port
ExecutorService ex= Executors.newFixedThreadPool(10);
for(int i=0;i<200;i++)
{
try {
Proxy proxy=getproxies(q1);//得到代理ip
vote vote=new vote(proxy);
ex.execute(vote);
}
catch (Exception e)
{e.printStackTrace();}
}
ex.shutdown();
}
static synchronized Proxy getproxies(Queue<proxynode> q1) throws IOException//上鎖得到代理,由於ip只用一次,也可以使用自帶的線程安全隊列 {
if(q1.size()<15)//擴充ip池
{
String jsonva=Jsoup.connect(ipurl).timeout(2500).get().text();
JSONObject jsonObject= JSON.parseObject(jsonva);
String code=(String) jsonObject.get("code");//狀態嗎
if (code.equals("0")) {//正常返回接口
JSONArray jsonArray = jsonObject.getJSONArray("msg");
for (Object jsonobj: jsonArray) {
JSONObject team = (JSONObject) jsonobj;
String ip = team.getString("ip");
int port = team.getInteger("port");
proxynode node=new proxynode(ip,port);
q1.add(node);
}
}
else return null;
}
proxynode proxynode=q1.poll();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxynode.ip, proxynode.port));
return proxy;
}
static class proxynode//一個node儲存ip和端口 {
String ip;
int port;
proxynode(String ip,int port)
{
this.ip=ip;
this.port=port;
}
}
static class vote implements Runnable{
Proxy proxy;
vote(Proxy proxy) {
this.proxy=proxy;
}
public void dovote() throws IOException {
//Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("host", 8888));
try {
Connection connect = Jsoup.connect("http://yunxin.163.com/api/vote/update").timeout(2000);
Map<String,String>date=new HashMap<String, String>();
date.put("id","17");
date.put("times","1");
date.put("activity","minichallenge1");
date.put("Referer","http://yunxin.163.com/promotion/minichallenge/gallery?from=groupmessage&isappinstalled=0");
date.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
connect.data(date);
connect.ignoreContentType(true);
connect.proxy(proxy);
Document doc=connect.post();
System.out.println(Thread.currentThread().getName()+" "+proxy.address()+" "+doc.text());
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
public void run() {
try {
for(int i=0;i<5;i++) {
dovote();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
複製代碼
結果
在寫爬蟲仍是python方便和簡單。由於py對json支持較好(字典),而java強對象類型語法要求較嚴。可是在多線程方面java確定是秒殺py的。由於py的多線程是(假)多線程。想提升速度的能夠研究多進程。
這類問題本質不難的,作過一次就很簡單了。這只是其中一種案例。提供一些思想和解決思路。遇到不一樣的問題可能須要不一樣的結構,方式去解決,這就須要融匯貫通。
若是有錯誤,請大佬指正。