cockroach 爬蟲:又一個 java 爬蟲實現

簡介

cockroach[小強] 當時不知道爲啥選了這麼個名字,又長又難記,致使編碼的過程當中由於單詞的拼寫問題耽誤了好長時間。css

這個項目算是個人又一個坑吧,算起來挖的坑多了去了,多一個很少少一個很多。html

一個小巧、靈活、健壯的爬蟲框架,暫且叫作框架吧。java

簡單到什麼程度呢,幾句話就能夠建立一個爬蟲。git

依賴

  • java8 程序中用到了一些 java8 的新特性
  • maven

下面就逐點介紹一下:github

小巧

git clone https://github.com/zhangyingwei/cockroach.gitredis

cd cockroach數據庫

git installcookie

而後新建一個 maven 項目,在 pom 文件中引入框架

<dependency>
    <groupId>com.zhangyingwei.cockroach</groupId>
    <artifactId>cockroach</artifactId>
    <version>1.0-Alpha</version>
</dependency>

在項目中新建一個測試類 App.java 並新建 main 方法。maven

實例

public static void main(String[] args){
    CockroachConfig config = new CockroachConfig()
                    .setAppName("我是一個小強")
                    .setThread(2); //爬蟲線程數
    CockroachContext context = new CockroachContext(config);
    TaskQueue queue = TaskQueue.of();
    context.start(queue);
    
    // 以上就是一個完整的爬蟲,下邊的代碼至關於一個生產者,往隊列裏邊寫任務,一旦寫入任務,爬蟲就會對任務進行爬取
    new Thread(() -> {
        int i = 0;
        while(true){
            i++;
            try {
                Thread.sleep(1000);
                String url = "http://www.xicidaili.com/wt/"+i;
                System.out.println(url);
                queue.push(new Task(url));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i > 1000) {
                break;
            }
        }
    }).start();
}

靈活

那靈活又體如今什麼方面呢

  • 能夠自定義 http 客戶端(可選,默認使用 okhttp3)
  • 能夠自定義結果的處理 (可選,默認使用打印處理器)

自定義 http 客戶端

首先咱們嘗試一下自定義客戶端

public class SelfHttpClient implements HttpClient {
       public HttpClient setProxy(HttpProxy proxy){
            //設置代理實現方法
       }
       public TaskResponse doGet(Task task) throws Exception{
            // get 請求實現方法
       }
   
       public HttpClient proxy(){
            // 應用代理到 http 客戶端 方法
       }
   
       public TaskResponse doPost(Task task) throws Exception{
            // post 請求實現方法
       }
   
       public HttpClient setCookie(String cookie){
            // 設置 cookie 實現方法
       }
   
       public HttpClient setHttpHeader(Map<String, String> httpHeader){
            // 設置 header 實現方法
       }
}

應用自定義 http 客戶端到爬蟲

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個小強")
    .setThread(2) //爬蟲線程數
    .setHttpClient(SelfHttpClient.class)

自定義結果處理類

自定義結果處理類

public class SelfStore implements IStore {
    @Override
    public void store(TaskResponse response) {
        System.out.println(response.getContent());
    }
}

這裏簡單的將結果打印了出來,在實際應用中,咱們能夠保存到數據庫或者保存到文件中等等。值得一說的是,若是結果是 html 網頁文本的話,咱們還提供了 select("css選擇器") 來對結果文本進行處理。

應用自定義 store 客戶端到爬蟲

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個小強")
    .setThread(2) //爬蟲線程數
    .setHttpClient(SelfHttpClient.class)
    .setStore(SelfStore.class);

自定義錯誤處理類

當 http 請求網頁出現錯誤的時候會統必定位到錯誤處理類,若是沒有自定義錯誤處理類,系統會默認使用 DefaultTaskErrorHandler ,此處理類會吧錯誤信息打印出來。具體實現代碼以下。

public class DefaultTaskErrorHandler implements ITaskErrorHandler {
    private Logger logger = Logger.getLogger(DefaultTaskErrorHandler.class);
    @Override
    public void error(Task task,String message) {
        logger.info("task error: "+message);
    }
}

若是須要自定義錯誤處理類,能夠仿照以上代碼,實現 ITaskErrorHandler 接口,在 error 方法中實現本身的處理邏輯。

在自定義錯誤處理類以後,咱們須要把自定義類應用到爬蟲。

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個小強")
    .setThread(2) //爬蟲線程數
    .setHttpClient(SelfHttpClient.class)
    .setStore(SelfStore.class)
    .setTaskErrorHandler(SelfTaskErrorHandler.class);

健壯

說到健壯,這裏主要體如今如下幾個方面:

應對IP封鎖

這裏咱們使用動態代理來解決這個問題。

動態代理的使用

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個小強")
    .setThread(2) //爬蟲線程數
    .setHttpClient(SelfHttpClient.class)
    .setProxys("100.100.100.100:8888,101.101.101.101:8888")

如上所示,咱們能夠設置若干個代理 ip,最終將全部代理 ip 生成一個代理池,在爬蟲請求以前,咱們會從代理池中隨機抽取一個 ip 作代理。

應對 http 請求中的 user-agent 問題

程序中實現了一個 user-agent 池,每次請求都會隨機取出一個 user-agent 使用,目前在程序中集成了 17 種 user-agent,後續會考慮把這塊開放出來到配置中,自定義配置(有沒有意義呢?)。

程序中的異常處理問題

目前在異常處理這塊,自己也不是很是擅長,已經盡力把異常控制在一個可控的範圍內,程序中定義了不少自定義異常,這裏沒有什麼發言權,就不細說了,各位要是有意見建議,歡迎拍磚。

所謂深度爬取

程序中並無現成的深度爬取實現,是由於通常狀況下我並不以爲深度爬取有什麼卵用,可是也不是沒有爲深度爬取留出來一席之地。咱們能夠本身提取出頁面中的連接並加入到任務隊列中。以達到深度爬取的效果。

public class DemoStore implements IStore {

    private String id = NameUtils.name(DemoStore.class);

    public DemoStore() throws IOException {}

    @Override
    public void store(TaskResponse response) throws IOException {
        List<String> urls = response.select("a").stream().map(element -> element.attr("href")).collect(Collectors.toList());
        try {
            response.getQueue().push(urls);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

關於分佈式,我有話說

如今網上是個爬蟲就要搞一下分佈式,這令我很不爽。

實際上我看過幾個所謂的分佈式爬蟲源碼,他們所謂的分佈式,連僞分佈式都算不上!!!使用個 redis 作消息中間件就分佈式了嗎? 這就是所謂的分佈式??這根本就不是分佈式,原本我也準備使用 redis 作消息中間件來裝個分佈式的 B,可是寫了一半突然以爲有點噁心,遂刪除了代碼,還程序一個清靜,也還我本身一個安心。

分佈式這個坑確定是要挖的!!!

因此,個人分佈式將會包括:

  • 分佈式消息中間件(有可能會使用 redis 或者本身實現一個; 爲了還程序一個清靜,最有可能會本身實現一個)
  • 分佈式任務調度
  • 分佈式分佈式容錯機制
  • 分佈式事務
  • 狀態監控

因此,這個坑是愈來愈大了麼??我靠,有點怕怕

相關文章
相關標籤/搜索