Java網路爬蟲(七)--實現定時爬取與IP代理池
阿新 • • 發佈:2019-01-06
定點爬取
當我們需要對金融行業的股票資訊進行爬取的時候,由於股票的價格是一直在變化的,我們不可能手動的去每天定時定點的執行程式,這個時候我們就需要實現定點爬取了,我們引入第三方庫quartz的使用:
package timeutils;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* Created by paranoid on 17-4-13.
*/
public class TimeUpdate {
public void go() throws Exception {
// 首先,必需要取得一個Scheduler的引用(設定一個工廠)
SchedulerFactory sf = new StdSchedulerFactory();
//從工廠裡面拿到一個scheduler例項
Scheduler sched = sf.getScheduler();
//真正執行的任務並不是Job介面的例項,而是用反射的方式例項化的一個JobDetail例項
JobDetail job = newJob(MyTimeJob.class).withIdentity("job1" , "group1").build();
// 定義一個觸發器,job 1將每隔執行一次
CronTrigger trigger = newTrigger().withIdentity("trigger1", "group1").
withSchedule(cronSchedule("50 47 17 * * ?")).build();
//執行任務和觸發器
Date ft = sched.scheduleJob(job, trigger);
//格式化日期顯示格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println(job.getKey() + " 已被安排執行於: " + sdf.format(ft) + "," +
"並且以如下重複規則重複執行: " + trigger.getCronExpression());
sched.start();
}
public static void main(String[] args) throws Exception {
TimeUpdate test = new TimeUpdate();
test.go();
}
}
在上面的程式碼中,已經詳細的給出了實現定時爬取的基本程式碼:
JobDetail job = newJob(MyTimeJob.class).withIdentity("job1", "group1").build();
這句程式碼中的MyTimeJob.class就是我們要執行的任務程式碼,它是通過類的反射載入機制進行執行的,之後我們設定它為第一組的第一個任務。
要使用這個第三方庫我們需要了解一些cron表示式的概念,網上由於對它的說明很多,我就不再這裡進行說明 ,大家可以看到:
cronSchedule("50 47 17 * * ?")
我設定的是每天的17:47:50秒執行這個程式。
值得注意的是:我們所要執行的任務必須寫在execute方法之中,在下面的程式碼就是一個例項,也就是我們需要實現的IP代理池。
IP代理池
在網上搜索了很多關於反爬蟲的機制,實用的還是IP代理池,我依照網上的思想自己寫了一個,大致的思路是這樣的:
- 首先我使用本機IP在xici(西刺)代理網站上的高匿IP代理區抓取了第一頁的代理IP放入了一個數組之中;
- 然後我使用陣列中的IP對要訪問的頁面進行輪番呼叫,每訪問一個頁面就換一個IP;
- 我將得到的IP按連結速度的快慢進行排序,選需速度最快的前100個;
- 我對得到的IP進行測試,如果不能使用就在容器中刪除;
- 將最終的IP寫入資料庫中。
實現IP代理池的主要邏輯程式碼如下:
package timeutils;
import IPModel.DatabaseMessage;
import IPModel.IPMessage;
import database.DataBaseDemo;
import htmlparse.URLFecter;
import ipfilter.IPFilter;
import ipfilter.IPUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static java.lang.System.out;
/**
* Created by paranoid on 17-4-13.
*/
public class MyTimeJob implements Job {
public void execute(JobExecutionContext argv) throws JobExecutionException {
List<String> Urls = new ArrayList<>();
List<DatabaseMessage> databaseMessages = new ArrayList<>();
List<IPMessage> list = new ArrayList<>();
List<IPMessage> ipMessages = new ArrayList<>();
String url = "http://www.xicidaili.com/nn/1";
String IPAddress;
String IPPort;
int k, j;
//首先使用本機ip進行爬取
try {
list = URLFecter.urlParse(url, list);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//對得到的IP進行篩選,選取連結速度前100名的
list = IPFilter.Filter(list);
//構造種子Url
for (int i = 1; i <= 5; i++) {
Urls.add("http://www.xicidaili.com/nn/" + i);
}
//得到所需要的資料
for (k = 0, j = 0; j < Urls.size(); k++) {
url = Urls.get(j);
IPAddress = list.get(k).getIPAddress();
IPPort = list.get(k).getIPPort();
//每次爬取前的大小
int preIPMessSize = ipMessages.size();
try {
ipMessages = URLFecter.urlParse(url, IPAddress, IPPort, ipMessages);
//每次爬取後的大小
int lastIPMessSize = ipMessages.size();
if(preIPMessSize != lastIPMessSize){
j++;
}
//對IP進行輪尋呼叫
if (k >= list.size()) {
k = 0;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//對得到的IP進行篩選,選取連結速度前100名的
ipMessages = IPFilter.Filter(ipMessages);
//對ip進行測試,不可用的從陣列中刪除
ipMessages = IPUtils.IPIsable(ipMessages);
for(IPMessage ipMessage : ipMessages){
out.println(ipMessage.getIPAddress());
out.println(ipMessage.getIPPort());
out.println(ipMessage.getServerAddress());
out.println(ipMessage.getIPType());
out.println(ipMessage.getIPSpeed());
}
//將得到的IP儲存在資料庫中(每次先清空資料庫)
try {
DataBaseDemo.delete();
DataBaseDemo.add(ipMessages);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//從資料庫中將IP取到
try {
databaseMessages = DataBaseDemo.query();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
for (DatabaseMessage databaseMessage: databaseMessages) {
out.println(databaseMessage.getId());
out.println(databaseMessage.getIPAddress());
out.println(databaseMessage.getIPPort());
out.println(databaseMessage.getServerAddress());
out.println(databaseMessage.getIPType());
out.println(databaseMessage.getIPSpeed());
}
}
}
整個IP代理池程式的實現架構如下:
- database包中包裝了資料庫的各種操作;
- htmlparse包中主要實現了對得到的html頁面的解析工作;
- httpbrowser包中主要實現了返回請求Url返回html頁面的工作;
- ipfilter包中主要實現了IP的過濾(速度可好)和檢測(是否可用);
- ipmodel中主要封裝了抓取ip的維度和從資料庫中拿到的ip的維度;
- timeutils主要實現了定點爬取和整體邏輯。
原始碼連結
有興趣的同學可以前往我的github上檢視整個專案的原始碼,程式碼量不多而且註釋也比較清晰,如果覺得不錯的話可以給個星哦~~