Java網絡爬蟲(七)--實現定時爬取與IP代理池

注:對代碼及思路進行了改進—Java網絡爬蟲(十一)–重構定時爬取以及IP代理池(多線程+Redis+代碼優化)


定點爬取

當我們需要對金融行業的股票信息進行爬取的時候,由於股票的價格是一直在變化的,我們不可能手動的去每天定時定點的運行程序,這個時候我們就需要實現定點爬取了,我們引入第三方庫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代理池,我依照網上的思想自己寫了一個,大致的思路是這樣的:

  1. 首先我使用本機IP在xici(西刺)代理網站上的高匿IP代理區抓取了第一頁的代理IP放入了一個數組之中;
  2. 然後我使用數組中的IP對要訪問的頁面進行輪番調用,每訪問一個頁面就換一個IP;
  3. 我將得到的IP按鏈接速度的快慢進行排序,選需速度最快的前100個;
  4. 我對得到的IP進行測試,如果不能使用就在容器中刪除;
  5. 將最終的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代理池程序的實現架構如下:

這裏寫圖片描述

  1. database包中包裝了數據庫的各種操作;
  2. htmlparse包中主要實現了對得到的html頁面的解析工作;
  3. httpbrowser包中主要實現了返回請求Url返回html頁面的工作;
  4. ipfilter包中主要實現了IP的過濾(速度可好)和檢測(是否可用);
  5. ipmodel中主要封裝了抓取ip的維度和從數據庫中拿到的ip的維度;
  6. timeutils主要實現了定點爬取和整體邏輯。

源碼鏈接

有興趣的同學可以前往我的github上查看整個項目的源碼,代碼量不多而且註釋也比較清晰,如果覺得不錯的話可以給個星哦~~

實現定時爬取與IP代理池

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章