簡易小爬蟲項目

最近疏於學習,乾脆就把去年跟同學做的職潮人小程序中的爬取職位這部分的代碼拿出來,加以改進瞎搞一波吧。

代碼Github地址: https://github.com/zhouhuanghua/project

之前的可以爬取拉勾BOSS智聯還有一些校招網站的,現在只拿拉勾的做個典型吧。

(#說起來也可惜,前後端都基本做完了,網站備案也弄好了,then不知道爲啥突然就沒了興趣......宣告失敗#)

說回主題,這次做完的效果是這樣的 (#說好的不再搞前端的,哎

看一下重點吧,這個是架構圖(鎮樓圖)

 

那麼,問題來了

1, 爲什麼需要RabbitMQ?

答:看圖中的幾個服務小夥子,各自的責任是分離的,我只幹我的你也不能影響我,所以拿到結果直接丟到MQ上面(強行解釋了MQ發揮着異步+解耦的作用)。

2, 爲什麼使用binlog處理增量數據?

答:項目結構裏,爬蟲系統和查詢平臺分在了不同的模塊,可以獨立運行,我不想它們之間共用一些東西。

代碼這裏只講一下里面用到的責任鏈設計模式。其它的話可以去GitHub克隆下來看(點擊這裏前往)。

定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係, 將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。之前的博客(點擊這裏前往)裏面有更詳細的介紹。

常見的有兩種寫法,也就是外部控制鏈路流向和內部控制鏈路流向。

外部控制的比較簡單,就是把所有處理對象收集起來遍歷,根據每個處理的返回值確定是否繼續和終止。僞代碼如下

List<Handler> handlerList = ...;
for (Handler h : handlerList) {
    R result = h.process(xxx);
    if (result xxx) {
        break;
    }
}

內部的就複雜一點,有點遞歸的意思,就是由處理者決定是否將請求交給下一個處理者處理。

首先定義處理者接口,抽象方法是crawl

public interface IStrategy {

    default boolean isNormalPage(Document document) {
        return Objects.nonNull(document.selectFirst("div[class=job-name]"));
    }

    void crawl(String url, ObjectWrapper<Document> docWrapper, CrawlStrategyChain strategyChain);
}

然後添加兩個處理者實現類,Jsoup和Chrome

public class JsoupStrategy implements IStrategy {

    private final Map<String, String> COMMON_HEADER_MAP = MapUtils.buildMap(
            "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
            "Accept", "application/json, text/plain, */*",
            "Cookie", "token");

    @Override
    public void crawl(String url, ObjectWrapper<Document> docWrapper, CrawlStrategyChain strategyChain) {
        try {
            Document document = Jsoup.connect(url).headers(COMMON_HEADER_MAP).get();
            if (isNormalPage(document)) {
                docWrapper.setObj(document);
                return;
            }
        } catch (Throwable t) {
            log.warn("Jsoup加載網頁[url={}]異常,t={}", url, ThrowableUtils.getStackTrace(t));
        }
        strategyChain.doCrawl(url, docWrapper);
    }
}
public class ChromeStrategy implements IStrategy {

    @Override
    public void crawl(String url, ObjectWrapper<Document> docWrapper, CrawlStrategyChain strategyChain) {
        WebDriver webDriver = BrowserDriverFactory.openChromeBrowser();
        try {
            webDriver.get(url);
            Document document = Jsoup.parse(webDriver.getPageSource());
            if (isNormalPage(document)) {
                docWrapper.setObj(document);
                return;
            }
        } catch (Throwable t) {
            log.warn("Chrome加載網頁[url={}]異常,t={}", url, ThrowableUtils.getStackTrace(t));
        } finally {
            OptionalOperationUtils.consumeIfNonNull(webDriver, WebDriver::quit);
        }
        strategyChain.doCrawl(url, docWrapper);
    }
}

接着,定義一個鏈調用對象

public final class CrawlStrategyChain {
    private int pos = 0;
    private int n;
    private IStrategy[] strategies;

    private CrawlStrategyChain() {
    }

    public static CrawlStrategyChain build(IStrategy[] strategies) {
        CrawlStrategyChain instance = new CrawlStrategyChain();
        instance.strategies = Objects.requireNonNull(strategies, "CrawlStrategyChain構造參數strategies不能爲空!");
        instance.n = strategies.length;
        return instance;
    }

    public void doCrawl(String url, ObjectWrapper<Document> docWrapper) {
        if (pos < n) {
            strategies[pos++].crawl(url, docWrapper, this);
        }
    }
}

最後,看下是怎麼使用的

聰明的你看明白了沒有呀?不明白的話沒關係,趕緊去下載代碼本地跑幾遍吧!

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