基於http(s)協議的模板化爬蟲設計

聲明:本文爲原創,轉載請註明出處

本文總共三章,前面兩章廢話吐槽比較多,想看結果的話,直接看第三章(後續會更新,最近忙着畢設呢,畢設也是我自己做的,關於射頻卡的,有時間我也放上來,哈哈)。

  • 一,系統總體結構

  首先吐槽一下,標題取得好水,原諒我是一枚耿直的工科男。系統框圖如下所示,簡單看一下,對整體有個把握,總體由4個部分組成

  上圖中,裝載URL隊列的是先進先出的隊列,整個爬蟲系統的設計就是基於寬度優先遍歷的原則設計的。所以,對於執行整個下載任務的引擎而言,它只是啓動一個線程池,然後機械的從隊列裏面拿出鏈接,下載網頁,然後再將網頁中的鏈接放到隊列裏面,循環上面的操作。由於每一個站點都有其自身佈局的特點,比如特定的編碼,特定的URL格式,面向站點的模板需要提供這些特點,以方便爬取效率和自己想要得到的效果。對於一張網頁,我們真正關心的,講個實話,只是一部分數據,所以在模板獲取數據之前,是先需要對數據進行過濾,排版等相關處理的,面向數據處理的模型就是爲了解決這個問題而產生的。(我去感覺這段話,有種催眠的感覺,發現自己表達能力好差。)

  •   二,設計思想及核心流程圖

   說點題外話,關於爬蟲的的實現,網上很多,但是隻要你不是我這樣的大老粗,你仔細看就會發現,所有的實現基本都是機械抓取URL,最爲致命的是抓取的URL還有可能是相對URL。那什麼獲取個性化數據就有點大海撈針的感覺了。哈哈,自戀一下,本爬蟲就可以大海撈針(你也可以當笑話看,否則這玩意太枯燥了)。其實我感覺的話,爬蟲的核心無非就兩個:一是要能很好的處理URL,二是方便獲取想要的數據。至於什麼效率,鄙人認爲,在這個計算機處理能力飛速提示的年代,不是那麼重要。

  下面是整個引擎執行流程圖:

上圖十分清楚的描述了整個引擎的執行流程圖,現在一看好像很簡單,但是當時寫起來,那個問題,不想說了,不利於弘揚社會主義核心價值觀,哈哈。至於到底是怎麼用我會把代碼放出來(留言qq索取也行,githup還沒有建倉庫呢),有了這個圖大家看起來,也方便些。接下來,實在是不想廢話了,直接看使用demo。

  • 三,實踐

  正所謂實踐是檢驗真理的唯一標準,這裏以抓取阿里巴巴旗下螞蟻金服的基金數據爲例子,進行簡單講解一下。

  3.1 定義一個基於該網站的引擎類。此類需要導入我開發的包,然後繼承自Spider。Spider有幾個函數是用於子類重載的。主要的也就是下面的三個重載子函數.詳情看註釋

public class AliFundtion extends Spider {
    private WebClient webClient=new WebClient();
    private Pattern HREF=Pattern.compile("href=\"(.*?)\"");
    public AliFundtion(int maxSize,int threadSize){
        super(maxSize,threadSize);
        initWebOptions();
    }
    private void initWebOptions(){
        webClient.setAjaxController(new NicelyResynchronizingAjaxController());
        webClient.getOptions().setJavaScriptEnabled(true);
        webClient.getOptions().setCssEnabled(false);
        webClient.getOptions().setTimeout(35000);
        webClient.getOptions().setThrowExceptionOnScriptError(false);
    }

    /**
     * 通過重載這個函數,我們這裏只抓取螞蟻金服首頁展示的基金數據,因爲它首頁包含了所有的基金,
     * 這個函數決定了URL隊列裏面到底有哪些鏈接,是引擎的抓取的來源
     * @param html 抓取到的網頁
     * @param url  該網頁對應的URL
     * @param que URL隊列 
     */
    @Override
    protected void allHrefEnQueue(String html, String url, SpiderQueue que) {
        if(url.equals("http://www.fund123.cn/fund")){//斷定是首頁
            List<String> hrefs=RegexUtil.getAllMatcher(html,1,HREF);
            for(String href:hrefs){
                String absoUrl=Browser.relative2TrUri(href,url);//轉化爲絕對路徑
                if(absoUrl.startsWith("http://www.fund123.cn/matiaria?fundCode")){//這正是我們需要的基金數據
                    que.enQueue(absoUrl);
                }
            }
        }

    }

    /**
     * 這個函數決定了,通過URL獲取網頁的方式,對於需要執行js代碼的網頁而言,重載這個函數是必須得,比如這裏就是基於htmlUnit重載了Spider的這個函數
     * @param url 即將抓取的URL
     * @param charset 所用的字符編碼
     * @return  返回抓取到的數據
     * @throws IOException 
     */
    @Override
    synchronized protected String toGetHtmlPage(String url, String charset) throws IOException {
       HtmlPage page=webClient.getPage(url);
        return page.asXml();
    }

    /**
     * 注意這裏必須調用父類的close()方法,來釋放一些資源
     */
    @Override
    protected void close() {
        super.close();
        webClient.close();
    }
}

 

 

   3.2 主程序調用步驟

public class Main {
    public static void main(String[] args) {

        //step1: 創建一個你自己定義的爬蟲引擎,第一個參數表示隊列的最大容量,第二個參數表示開啓線程池中線程數量
        AliFundtion aliFundtion=new AliFundtion(50,6);
        //step2:定義你自己的站點模板
        ICrawlTemplate temp=new AbstractTemplate() {
            @Override
            public void crawlValue(String html, String url) {
                //todo 在這裏寫下你想做的事,比如將得到的數據持久化到數據庫,或者保存到磁盤,其中html表示被數據模型修飾後的數據,url是該html對應的鏈接
                System.out.println(html);
            }
            @Override
            public String getBaseSite() {
                return "http://www.fund123.cn/fund";
            }

            @Override
            public String getCharset() {
                return null;
            }

            @Override
            public boolean filterUrl(String url) {
                return true;
            }
        };
        //step3:採用正則表達式進行數據建模
            //<_%></_%>這是一對錶示這個一個正則表達式的標籤
                //index 屬性 就是該正則表達式需要提取的組
        StringBuilder sb=new StringBuilder();
        sb.append("基金名稱:").append("<_% index=\"1\">").append("<span class=\"fundmatiaria-title-fundname\">(.*?)</span>").append("</_%>");
        sb.append("基金代碼:").append("<_% index=\"1\">").append("<span class=\"fundmatiaria-title-fundcode\">(.*?)</span>").append("</_%>");
        sb.append("基金淨值:").append("<_% index=\"1\">").append("<p class=\"fundmatiaria-fundinfo-value\".*?>(.*?)</p>").append("</_%>");
        RegexModel model=new RegexModel(sb.toString());
        model.getOptions().setAbsoJs(true);//表示將所有js路徑轉化爲絕對路徑
        model.getOptions().setAbsoImage(true);//表示將所有image路徑轉化爲絕對路徑
        model.getOptions().setAbsoCss(true);//表示將所有css路徑轉化爲絕對路徑
        aliFundtion.setModel(model);
        //step4:下載模板
        aliFundtion.downLoadArtcle(temp);
    }
}

 

 3.3 看一下結果:

基金名稱:南方原油(QDII-FOF)

基金代碼:(501018)

基金淨值:1.1502  +1.42%  -1.57%

基金名稱:廣發道瓊斯美國石油開發與生產指數(QDII-LOF)A

基金代碼:(162719)

基金淨值:1.1371 +1.65% +0.04%

 

基金名稱:銀華心誠靈活配置混合

基金代碼:(005543)

基金淨值:0.9507 +1.79% +1.79%

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