cockroach 爬蟲(chóng):又一個(gè) java 爬蟲(chóng)實(shí)現(xiàn)

項(xiàng)目地址: cockroach

求個(gè)位好心人,各位哥哥姐姐順手甩幾個(gè) star 給我吧?。?!

簡(jiǎn)介

cockroach[小強(qiáng)] 當(dāng)時(shí)不知道為啥選了這么個(gè)名字,又長(zhǎng)又難記,導(dǎo)致編碼的過(guò)程中因?yàn)閱卧~的拼寫(xiě)問(wèn)題耽誤了好長(zhǎng)時(shí)間。

這個(gè)項(xiàng)目算是我的又一個(gè)坑吧,算起來(lái)挖的坑多了去了,多一個(gè)不多少一個(gè)不少。

一個(gè)小巧、靈活、健壯的爬蟲(chóng)框架,暫且叫做框架吧。

簡(jiǎn)單到什么程度呢,幾句話就可以創(chuàng)建一個(gè)爬蟲(chóng)。

依賴

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

下面就逐點(diǎn)介紹一下:

小巧

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

cd cockroach

git install

然后新建一個(gè) maven 項(xiàng)目,在 pom 文件中引入

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

在項(xiàng)目中新建一個(gè)測(cè)試類 App.java 并新建 main 方法。

實(shí)例

public static void main(String[] args){
    CockroachConfig config = new CockroachConfig()
                    .setAppName("我是一個(gè)小強(qiáng)")
                    .setThread(2); //爬蟲(chóng)線程數(shù)
    CockroachContext context = new CockroachContext(config);
    TaskQueue queue = TaskQueue.of();
    context.start(queue);
    
    // 以上就是一個(gè)完整的爬蟲(chóng),下邊的代碼相當(dāng)于一個(gè)生產(chǎn)者,往隊(duì)列里邊寫(xiě)任務(wù),一旦寫(xiě)入任務(wù),爬蟲(chóng)就會(huì)對(duì)任務(wù)進(jìn)行爬取
    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();
}

靈活

那靈活又體現(xiàn)在什么方面呢

  • 可以自定義 http 客戶端(可選,默認(rèn)使用 okhttp3)
  • 可以自定義結(jié)果的處理 (可選,默認(rèn)使用打印處理器)

自定義 http 客戶端

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

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

應(yīng)用自定義 http 客戶端到爬蟲(chóng)

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個(gè)小強(qiáng)")
    .setThread(2) //爬蟲(chóng)線程數(shù)
    .setHttpClient(SelfHttpClient.class)

自定義結(jié)果處理類

自定義結(jié)果處理類

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

這里簡(jiǎn)單的將結(jié)果打印了出來(lái),在實(shí)際應(yīng)用中,我們可以保存到數(shù)據(jù)庫(kù)或者保存到文件中等等。值得一說(shuō)的是,如果結(jié)果是 html 網(wǎng)頁(yè)文本的話,我們還提供了 select("css選擇器") 來(lái)對(duì)結(jié)果文本進(jìn)行處理。

應(yīng)用自定義 store 客戶端到爬蟲(chóng)

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個(gè)小強(qiáng)")
    .setThread(2) //爬蟲(chóng)線程數(shù)
    .setHttpClient(SelfHttpClient.class)
    .setStore(SelfStore.class);

自定義錯(cuò)誤處理類

當(dāng) http 請(qǐng)求網(wǎng)頁(yè)出現(xiàn)錯(cuò)誤的時(shí)候會(huì)統(tǒng)一定位到錯(cuò)誤處理類,如果沒(méi)有自定義錯(cuò)誤處理類,系統(tǒng)會(huì)默認(rèn)使用 DefaultTaskErrorHandler ,此處理類會(huì)吧錯(cuò)誤信息打印出來(lái)。具體實(shí)現(xiàn)代碼如下。

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);
    }
}

如果需要自定義錯(cuò)誤處理類,可以仿照以上代碼,實(shí)現(xiàn) ITaskErrorHandler 接口,在 error 方法中實(shí)現(xiàn)自己的處理邏輯。

在自定義錯(cuò)誤處理類之后,我們需要把自定義類應(yīng)用到爬蟲(chóng)。

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個(gè)小強(qiáng)")
    .setThread(2) //爬蟲(chóng)線程數(shù)
    .setHttpClient(SelfHttpClient.class)
    .setStore(SelfStore.class)
    .setTaskErrorHandler(SelfTaskErrorHandler.class);

健壯

說(shuō)到健壯,這里主要體現(xiàn)在以下幾個(gè)方面:

應(yīng)對(duì)IP封鎖

這里我們使用動(dòng)態(tài)代理來(lái)解決這個(gè)問(wèn)題。

動(dòng)態(tài)代理的使用

CockroachConfig config = new CockroachConfig()
    .setAppName("我是一個(gè)小強(qiáng)")
    .setThread(2) //爬蟲(chóng)線程數(shù)
    .setHttpClient(SelfHttpClient.class)
    .setProxys("100.100.100.100:8888,101.101.101.101:8888")

如上所示,我們可以設(shè)置若干個(gè)代理 ip,最終將所有代理 ip 生成一個(gè)代理池,在爬蟲(chóng)請(qǐng)求之前,我們會(huì)從代理池中隨機(jī)抽取一個(gè) ip 做代理。

應(yīng)對(duì) http 請(qǐng)求中的 user-agent 問(wèn)題

程序中實(shí)現(xiàn)了一個(gè) user-agent 池,每次請(qǐng)求都會(huì)隨機(jī)取出一個(gè) user-agent 使用,目前在程序中集成了 17 種 user-agent,后續(xù)會(huì)考慮把這塊開(kāi)放出來(lái)到配置中,自定義配置(有沒(méi)有意義呢?)。

程序中的異常處理問(wèn)題

目前在異常處理這塊,本身也不是非常擅長(zhǎng),已經(jīng)盡力把異??刂圃谝粋€(gè)可控的范圍內(nèi),程序中定義了很多自定義異常,這里沒(méi)有什么發(fā)言權(quán),就不細(xì)說(shuō)了,各位要是有意見(jiàn)建議,歡迎拍磚。

所謂深度爬取

程序中并沒(méi)有現(xiàn)成的深度爬取實(shí)現(xiàn),是因?yàn)橐话闱闆r下我并不覺(jué)得深度爬取有什么卵用,但是也不是沒(méi)有為深度爬取留出來(lái)一席之地。我們可以自己提取出頁(yè)面中的鏈接并加入到任務(wù)隊(duì)列中。以達(dá)到深度爬取的效果。

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();
        }
    }
}

關(guān)于分布式,我有話說(shuō)

現(xiàn)在網(wǎng)上是個(gè)爬蟲(chóng)就要搞一下分布式,這令我很不爽。

實(shí)際上我看過(guò)幾個(gè)所謂的分布式爬蟲(chóng)源碼,他們所謂的分布式,連偽分布式都算不上?。?!使用個(gè) redis 做消息中間件就分布式了嗎? 這就是所謂的分布式??這根本就不是分布式,本來(lái)我也準(zhǔn)備使用 redis 做消息中間件來(lái)裝個(gè)分布式的 B,但是寫(xiě)了一半忽然覺(jué)得有點(diǎn)惡心,遂刪除了代碼,還程序一個(gè)清靜,也還我自己一個(gè)安心。

分布式這個(gè)坑肯定是要挖的?。?!

所以,我的分布式將會(huì)包括:

  • 分布式消息中間件(有可能會(huì)使用 redis 或者自己實(shí)現(xiàn)一個(gè); 為了還程序一個(gè)清靜,最有可能會(huì)自己實(shí)現(xiàn)一個(gè))
  • 分布式任務(wù)調(diào)度
  • 分布式分布式容錯(cuò)機(jī)制
  • 分布式事務(wù)
  • 狀態(tài)監(jiān)控

所以,這個(gè)坑是越來(lái)越大了么??我靠,有點(diǎn)怕怕

聯(lián)系方式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,715評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評(píng)論 25 708
  • 北方的秋天來(lái)的早,酷暑褪去,涼涼風(fēng)吹著發(fā)梢,秋意來(lái)襲,瓜熟香溢四處。核桃熟了,落了。 “阮阮,爺爺在前面的核桃樹(shù)下...
    月elian閱讀 465評(píng)論 0 0
  • tribbie閱讀 223評(píng)論 0 5
  • 1.編碼解碼程序 以后在寫(xiě)程序中經(jīng)常會(huì)用到,
    你說(shuō)你要一場(chǎng)閱讀 184評(píng)論 0 0

友情鏈接更多精彩內(nèi)容