前言
相信大家平時肯定會收到朋友發來的鏈接,打開一看,哦,需要投票。投完票后彈出一個頁面(恭喜您,您已經投票成功),再次點擊的時候發現,啊哈,您的IP(***.***.***.***)已經投過票了,不能重復投票。這時候,我們可能會想,能不能突破ip地址的限制進行刷票呢?有了這樣的想法,那就去做吧,下面我將介紹我這個簡單的刷票系統,僅供有需求的園友們參考。
1.系統設計
系統主要實現的是突破IP限制進行刷票,其中,由IP采集模塊負責從互聯網上爬取代理IP,放入阻塞隊列,該任務會定期執行。之后由投票模塊從阻塞隊列中獲取IP,並進行設置,然后進行投票。系統流程圖如下:

2.系統技術
系統使用HttpClient + JSoup + 多線程來完成刷票,HttpClient用於進行投票,JSoup用於解析頁面,多線程技術用於分離任務,使得分工更加明確。使用到了生產者消費者模式,該模式直接使用BlockingQueue來實現。
3、系統介紹
系統主要分為三個模塊:
①.IP采集模塊
②.投票模塊
③.IP信息模塊
其中,IP采集模塊主要是從互聯網爬取IP代理信息,並將該信息放入阻塞隊列,這樣就可以偽造IP,進行多次投票。
其中,投票模塊從IP采集模塊放入阻塞隊列取出IP信息,並設置代理,找到投票入口地址,然后進行投票操作。
其中,IP信息模塊主要是對爬取的IP信息進行了封裝,方便其他模塊進行操作。
3.1.IP采集模塊
IP采集模塊流程圖如下

幾點說明:
1.系統使用的代理IP站點URL為http://www.kuaidaili.com/,www.xicidaili.com。
2.提取IP信息為提取單條IP信息,並判斷歷史IP表是否已經存在,若存在,表示之前已經加入過此IP信息,則直接丟棄,反之,則加入隊列並加入歷史IP表。
3.此任務會定期開啟,如一個小時爬取一次代理IP。
3.2.投票模塊
投票模塊流程圖如下

幾點說明:
1.投票網站http://www.hnxdf.com/vote/,我們選取的第一位進行投票,分析出投票的入口為http://www.hnxdf.com/vote/iRadio_vote.asp?VoTeid=215。
2.根據IP采集模塊放入隊列的IP信息進行設置,然后進行投票。
3.3.IP信息模塊
此模塊主要對從網站爬取的IP信息進行了封裝,方便其他模塊進行操作。
4、系統代碼框架
系統的整個代碼框架如下

其中,bean包的IpInfo封裝了爬取的IP信息。
其中,entrance包的Vote為系統的入口。
其中,thread包的IPCollectTask為爬取代理IP任務,VoteThread為進行投票線程。
5.系統代碼
1.IpInfo.java
package com.hust.grid.leesf.bean;
public class IpInfo {
public IpInfo(String ipAddress, int port, String location,
String anonymousType, String type, String confirmTime) {
this(ipAddress, port, location, anonymousType, type, confirmTime, null,
null);
}
public IpInfo(String ipAddress, int port, String location,
String anonymousType, String type, String confirmTime,
String getPostSupport, String responseSpeed) {
this.ipAddress = ipAddress;
this.port = port;
this.location = location;
this.anonymousType = anonymousType;
this.type = type;
this.confirmTime = confirmTime;
this.getPostSupport = getPostSupport;
this.responseSpeed = responseSpeed;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getAnonymousType() {
return anonymousType;
}
public void setAnonymousType(String anonymousType) {
this.anonymousType = anonymousType;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getConfirmTime() {
return confirmTime;
}
public void setConfirmTime(String confirmTime) {
this.confirmTime = confirmTime;
}
public String getGetPostSupport() {
return getPostSupport;
}
public void setGetPostSupport(String getPostSupport) {
this.getPostSupport = getPostSupport;
}
public String getResponseSpeed() {
return responseSpeed;
}
public void setResponseSpeed(String responseSpeed) {
this.responseSpeed = responseSpeed;
}
@Override
public boolean equals(Object anthor) {
if (this == anthor) {
return true;
}
if (anthor == null || getClass() != anthor.getClass()) {
return false;
}
IpInfo ipInfo = (IpInfo) anthor;
return (this.ipAddress.equals(ipInfo.ipAddress)
&& this.port == ipInfo.port
&& this.location.equals(ipInfo.location)
&& this.anonymousType.equals(ipInfo.anonymousType)
&& this.type.equals(ipInfo.type) && this.confirmTime
.equals(ipInfo.confirmTime))
&& this.getPostSupport.equals(ipInfo.getPostSupport)
&& this.responseSpeed.equals(ipInfo.responseSpeed);
}
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash
+ (this.ipAddress != null ? this.ipAddress.hashCode() : 0);
hash = 89 * hash + this.port;
hash = 89 * hash
+ (this.location != null ? this.location.hashCode() : 0);
hash = 89
* hash
+ (this.anonymousType != null ? this.anonymousType.hashCode()
: 0);
hash = 89 * hash + (this.type != null ? this.type.hashCode() : 0);
hash = 89 * hash
+ (this.confirmTime != null ? this.confirmTime.hashCode() : 0);
hash = 89
* hash
+ (this.getPostSupport != null ? this.getPostSupport.hashCode()
: 0);
hash = 89
* hash
+ (this.responseSpeed != null ? this.responseSpeed.hashCode()
: 0);
return hash;
}
@Override
public String toString() {
return "ipAddress = " + ipAddress + ", port = " + port + ", localtion = "
+ location + ", anonymousType = " + anonymousType + ", type = "
+ type + ", confirmTime = " + confirmTime + ", getPostSupport = "
+ getPostSupport + ", responseSpeed = " + responseSpeed;
}
private String ipAddress;
private int port;
private String location;
private String anonymousType;
private String type;
private String confirmTime;
private String getPostSupport;
private String responseSpeed;
}
2.Vote.java
package com.hust.grid.leesf.entrance;
import java.util.Timer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import com.hust.grid.leesf.bean.IpInfo;
import com.hust.grid.leesf.thread.IPCollectTask;
import com.hust.grid.leesf.thread.VoteThread;
public class Vote {
private BlockingQueue<IpInfo> ipInfoQueue;
private IPCollectTask ipCollectTask;
private VoteThread voteThread;
public Vote() {
ipInfoQueue = new LinkedBlockingQueue<IpInfo>();
ipCollectTask = new IPCollectTask(ipInfoQueue);
voteThread = new VoteThread(ipInfoQueue);
}
public void vote() {
Timer timer = new Timer();
long delay = 0;
long period = 1000 * 60 * 60;
// 每一個小時采集一次ip
timer.scheduleAtFixedRate(ipCollectTask, delay, period);
// 開啟投票任務
voteThread.start();
}
public static void main(String[] args) {
Vote vote = new Vote();
vote.vote();
}
}
3.IPCollectTask.java
package com.hust.grid.leesf.thread;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.hust.grid.leesf.bean.IpInfo;
public class IPCollectTask extends TimerTask {
private BlockingQueue<IpInfo> ipInfoQueue; // 連接生產者與消費者的阻塞隊列
private List<IpInfo> historyIpLists; // 記錄已經獲取的ip信息
public IPCollectTask(BlockingQueue<IpInfo> ipInfoQueue) {
this.ipInfoQueue = ipInfoQueue;
this.historyIpLists = new ArrayList<IpInfo>();
}
/**
* 獲取www.xicidaili.com的ip地址信息
*/
public void getXiCiDaiLiIpLists() {
String url = "http://www.xicidaili.com/";
String host = "www.xicidaili.com";
Document doc = getDocumentByUrl(url, host);
// 解析頁面的ip信息
parseXiCiDaiLiIpLists(doc);
}
/**
* 解析頁面的ip信息
*
* @param doc
*/
public void parseXiCiDaiLiIpLists(Document doc) {
Elements eleLists = doc.getElementsByTag("tbody");
Element tbody = eleLists.get(0); // 獲取tbody
Elements trLists = tbody.children();
Element ele = null;
for (int i = 0; i < trLists.size(); i++) {
if ((i % 22 == 0) || (i % 22 == 1)) { // 去掉不符合條件的項
continue;
}
ele = trLists.get(i);
Elements childrenList = ele.children();
String ipAddress = childrenList.get(1).text();
int port = Integer.parseInt(childrenList.get(2).text());
String location = childrenList.get(3).text();
String anonymousType = childrenList.get(4).text();
String type = childrenList.get(5).text();
String confirmTime = childrenList.get(6).text();
IpInfo ipInfo = new IpInfo(ipAddress, port, location,
anonymousType, type, confirmTime);
putIpInfo(ipInfo);
}
}
/**
* 將ip信息放入隊列和歷史記錄中
*
* @param ipInfo
*/
private void putIpInfo(IpInfo ipInfo) {
if (!historyIpLists.contains(ipInfo)) { // 若歷史記錄中不包含ip信息,則加入隊列中
// 加入到阻塞隊列中,用作生產者
try {
ipInfoQueue.put(ipInfo);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 加入歷史記錄中
historyIpLists.add(ipInfo);
}
}
/**
* 根據網頁Document解析出ip地址信息
*
* @param doc
*/
private void parseKuaiDaiLiIpLists(Document doc) {
Elements eleLists = doc.getElementsByTag("tbody");
Element tbody = eleLists.get(0); // 獲取tbody
Elements trLists = tbody.children(); // 獲取十條ip記錄
for (Element tr : trLists) { // 遍歷tr
Elements tdElements = tr.children(); // tr中的td包含了具體的信息
String ipAddress = tdElements.get(0).text();
int port = Integer.parseInt(tdElements.get(1).text());
String anonymousType = tdElements.get(2).text();
String type = tdElements.get(3).text();
String getPostSupport = tdElements.get(4).text();
String location = tdElements.get(5).text();
String responseSpeed = tdElements.get(6).text();
String confirmTime = tdElements.get(7).text();
IpInfo ipInfo = new IpInfo(ipAddress, port, location,
anonymousType, type, confirmTime, getPostSupport,
responseSpeed);
putIpInfo(ipInfo);
}
}
/**
* 根據提供的url和host來獲取頁面信息
*
* @param url
* @param host
* @return
*/
private Document getDocumentByUrl(String url, String host) {
Document doc = null;
try {
doc = Jsoup
.connect(url)
.header("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0")
.header("Host", host).timeout(5000).get();
} catch (IOException e) {
e.printStackTrace();
}
return doc;
}
/**
* 獲取http://www.kuaidaili.com/free/的ip
*/
private void getKuaiDaiLiFreeIpLists() {
// 第一次訪問,需解析總共多少頁
String baseUrl = "http://www.kuaidaili.com/free/inha/";
String host = "www.kuaidaili.com";
Document doc = getDocumentByUrl(baseUrl, host);
// 解析ip信息
parseKuaiDaiLiIpLists(doc);
Element listNav = doc.getElementById("listnav");
// 獲取listnav下的li列表
Elements liLists = listNav.children().get(0).children();
// 獲取含有多少頁的子元素
Element pageNumberEle = liLists.get(liLists.size() - 2);
// 解析有多少頁
int pageNumber = Integer.parseInt(pageNumberEle.text());
// 拼接成其他頁的訪問地址
for (int index = 1; index <= pageNumber; index++) {
baseUrl = baseUrl + index;
doc = getDocumentByUrl(baseUrl, host);
parseKuaiDaiLiIpLists(doc);
// 休眠一秒
fallSleep(1);
}
}
/**
* 獲取www.kuaidaili.com/proxylist/的ip
*/
private void getKuaiDaiLiIpLists() {
int start = 1;
String baseUrl = "http://www.kuaidaili.com/proxylist/";
String host = "www.kuaidaili.com";
while (start <= 10) { // 爬取10頁
String url = baseUrl + start + "/";
Document doc = getDocumentByUrl(url, host);
// 解析ip信息
parseKuaiDaiLiIpLists(doc);
start++;
// 休眠一秒
fallSleep(1);
}
}
/**
* 進行休眠
*/
private void fallSleep(long seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// getKuaiDaiLiFreeIpLists();
System.out.println("IPCollect task is running");
getKuaiDaiLiIpLists();
getXiCiDaiLiIpLists();
}
public BlockingQueue<IpInfo> getIpInfoQueue() {
return ipInfoQueue;
}
public static void main(String[] args) {
BlockingQueue<IpInfo> queue = new LinkedBlockingQueue<IpInfo>();
IPCollectTask task = new IPCollectTask(queue);
Thread thread = new Thread(task);
thread.start();
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("queue size is " + queue.size());
try {
while (!queue.isEmpty()) {
System.out.println(queue.take());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("historyList size is " + task.historyIpLists.size());
}
}
4.VoteThread.java
package com.hust.grid.leesf.thread;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import com.hust.grid.leesf.bean.IpInfo;
public class VoteThread extends Thread {
private BlockingQueue<IpInfo> ipInfoQueue;
public VoteThread(BlockingQueue<IpInfo> ipInfoQueue) {
this.ipInfoQueue = ipInfoQueue;
}
@Override
public void run() {
HttpClient client = new DefaultHttpClient();
HttpParams params = client.getParams();
HttpConnectionParams.setConnectionTimeout(params, 10000);
HttpConnectionParams.setSoTimeout(params, 15000);
HttpResponse response = null;
HttpGet get = null;
HttpEntity entity = null;
HttpHost proxy = null;
while (true) {
IpInfo ipInfo = null;
try {
ipInfo = ipInfoQueue.take();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
proxy = new HttpHost(ipInfo.getIpAddress(), ipInfo.getPort());
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);
get = new HttpGet(
"http://www.hnxdf.com/vote/iRadio_vote.asp?VoTeid=215");
get.addHeader("Host", "www.hnxdf.com");
get.addHeader("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0");
try {
response = client.execute(get);
entity = response.getEntity();
byte[] bytes = EntityUtils.toByteArray(entity);
// 對響應內容編碼格式進行轉化,統一成utf-8格式
String temp = new String(bytes, "gbk");
byte[] contentData = temp.getBytes("utf-8");
System.out.println(new String(contentData));
System.out.println("-----------------------------------");
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6.系統總結
此系統很簡單,想清楚思路之后很快就能夠寫出代碼,系統運行時,由於代理IP站點提供的免費IP質量不是太高,有效的IP地址還是很少,所有效果不是特別理想,此系統功能也很簡單,但是各位園友可以在此基礎上去發揮自己的想象力,定制屬於自己的投票系統。
結束語
至此,整個系統分析就已經完成了,其中,圖也畫得不是太規范,還請各位園友海涵。也謝謝各位園友觀看。
ps:整個工程(包含必要的jar文件)已經上傳到GitHub上,歡迎各位園友訪問:https://github.com/leesf/TicketBrushSystem
http://www.cnblogs.com/leesf456/p/5170212.html

