天天看點

Webmagic 一個網絡爬蟲工具包

webmagic是一個開源的Java垂直爬蟲架構,目标是簡化爬蟲的開發流程,讓開發者專注于邏輯功能的開發。webmagic的核心非常簡單,但是覆寫爬蟲的整個流程,也是很好的學習爬蟲開發的材料。作者曾經進行過一年的垂直爬蟲的開發,webmagic就是為了解決爬蟲開發的一些重複勞動而産生的架構。

web爬蟲是一種技術,webmagic緻力于将這種技術的實作成本降低,但是出于對資源提供者的尊重,webmagic不會做反封鎖的事情,包括:驗證碼破解、代理切換、自動登入、抓取靜态資源等。

webmagic的架構和設計參考了以下兩個項目,感謝以下兩個項目的作者:

python爬蟲 scrapy https://github.com/scrapy/scrapy

Java爬蟲 Spiderman https://gitcafe.com/laiweiwei/Spiderman

webmagic遵循Apache 2.0協定,你可以自由進行使用和修改。有使用不便或者問題,歡迎在github送出issue,或者在oschina讨論子產品提問。

快速開始

使用maven

webmagic使用maven管理依賴,你可以直接下載下傳webmagic源碼進行編譯:

git clone https://github.com/code4craft/webmagic.git
mvn clean install
           

安裝後,在項目中添加對應的依賴即可使用webmagic:

<dependency>
        <groupId>us.codecraft</groupId>
        <artifactId>webmagic-core</artifactId>
        <version>0.2.0</version>
    </dependency>
    <dependency>
        <groupId>us.codecraft</groupId>
        <artifactId>webmagic-extension</artifactId>
        <version>0.2.0</version>
    </dependency>
           

項目結構

webmagic主要包括兩個包:

  • webmagic-core

    webmagic核心部分,隻包含爬蟲基本子產品和基本抽取器。webmagic-core的目标是成為網頁爬蟲的一個教科書般的實作。

  • webmagic-extension

    webmagic的擴充子產品,提供一些更友善的編寫爬蟲的工具。包括注解格式定義爬蟲、JSON、分布式等支援。

webmagic還包含兩個可用的擴充包,因為這兩個包都依賴了比較重量級的工具,是以從主要包中抽離出來:

  • webmagic-saxon

    webmagic與Saxon結合的子產品。Saxon是一個XPath、XSLT的解析工具,webmagic依賴Saxon來進行XPath2.0文法解析支援。

  • webmagic-selenium

    webmagic與Selenium結合的子產品。Selenium是一個模拟浏覽器進行頁面渲染的工具,webmagic依賴Selenium進行動态頁面的抓取。

在項目中,你可以根據需要依賴不同的包。

不使用maven

<<<<<<< HEAD

不使用maven的使用者,可以下載下傳這個二進制打包版本(感謝oschina):

=======

不使用maven的使用者,可以下載下傳附帶二進制jar包的版本(感謝oschina):

>>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637

git clone http://git.oschina.net/flashsword20/webmagic-bin.git
           

在**bin/lib**目錄下,有項目依賴的所有jar包,直接在IDE裡import即可。

第一個爬蟲

定制PageProcessor

PageProcessor是webmagic-core的一部分,定制一個PageProcessor即可實作自己的爬蟲邏輯。以下是抓取osc部落格的一段代碼:

public class OschinaBlogPageProcesser implements PageProcessor {

    private Site site = Site.me().setDomain("my.oschina.net")
       .addStartUrl("http://my.oschina.net/flashsword/blog");

    @Override
    public void process(Page page) {
        List<String> links = page.getHtml().links().regex("http://my\\.oschina\\.net/flashsword/blog/\\d+").all();
        page.addTargetRequests(links);
        page.putField("title", page.getHtml().xpath("//div[@class='BlogEntity']/div[@class='BlogTitle']/h1").toString());
        page.putField("content", page.getHtml().$("div.content").toString());
        page.putField("tags",page.getHtml().xpath("//div[@class='BlogTags']/a/text()").all());
    }

    @Override
    public Site getSite() {
        return site;

    }

    public static void main(String[] args) {
        Spider.create(new OschinaBlogPageProcesser())
             .pipeline(new ConsolePipeline()).run();
    }
}
           

這裡通過page.addTargetRequests()方法來增加要抓取的URL,并通過page.putField()來儲存抽取結果。page.getHtml().xpath()則是按照某個規則對結果進行抽取,這裡抽取支援鍊式調用。調用結束後,toString()表示轉化為單個String,all()則轉化為一個String清單。

Spider是爬蟲的入口類。Pipeline是結果輸出和持久化的接口,這裡ConsolePipeline表示結果輸出到控制台。

執行這個main方法,即可在控制台看到抓取結果。webmagic預設有3秒抓取間隔,請耐心等待。

使用注解

webmagic-extension包括了注解方式編寫爬蟲的方法,隻需基于一個POJO增加注解即可完成一個爬蟲。以下仍然是抓取oschina部落格的一段代碼,功能與OschinaBlogPageProcesser完全相同:

@TargetUrl("http://my.oschina.net/flashsword/blog/\\d+")
public class OschinaBlog {

    @ExtractBy("//title")
    private String title;

    @ExtractBy(value = "div.BlogContent",type = ExtractBy.Type.Css)
    private String content;

    @ExtractBy(value = "//div[@class='BlogTags']/a/text()", multi = true)
    private List<String> tags;

    public static void main(String[] args) {
        OOSpider.create(
            Site.me().addStartUrl("http://my.oschina.net/flashsword/blog"),
            new ConsolePageModelPipeline(), OschinaBlog.class).run();
    }
}
           

這個例子定義了一個Model類,Model類的字段'title'、'content'、'tags'均為要抽取的屬性。這個類在Pipeline裡是可以複用的。

注解的詳細使用方式見後文中得webmagic-extension注解子產品。

webmagic-core

webmagic-core是爬蟲的核心架構,隻包括一個爬蟲各功能子產品的核心功能。webmagic-core的目标是成為網頁爬蟲的一個教科書般的實作。

此節部分内容摘自作者的博文 webmagic的設計機制及原理-如何開發一個Java爬蟲。

webmagic-core的子產品劃分

webmagic-core參考了scrapy的子產品劃分,分為Spider(整個爬蟲的排程架構)、Downloader(頁面下載下傳)、PageProcessor(連結提取和頁面分析)、Scheduler(URL管理)、Pipeline(離線分析和持久化)幾部分。隻不過scrapy通過middleware實作擴充,而webmagic則通過定義這幾個接口,并将其不同的實作注入主架構類Spider來實作擴充。

Webmagic 一個網絡爬蟲工具包

Spider類(核心排程)

Spider是爬蟲的入口類,Spider的接口調用采用了鍊式的API設計,其他功能全部通過接口注入Spider實作,下面是啟動一個比較複雜的Spider的例子。

Spider.create(sinaBlogProcessor)
.scheduler(new FileCacheQueueScheduler("/data/temp/webmagic/cache/"))
.pipeline(new FilePipeline())
.thread(10).run();  
           

Spider的核心處理流程非常簡單,代碼如下:

<!-- lang: java -->
private void processRequest(Request request) {
    Page page = downloader.download(request, this);
    if (page == null) {
        sleep(site.getSleepTime());
        return;
    }
    pageProcessor.process(page);
    addRequest(page);
    for (Pipeline pipeline : pipelines) {
        pipeline.process(page, this);
    }
    sleep(site.getSleepTime());
}
           

<<<<<<< HEAD =======

Spider還包括一個方法test(String url),該方法隻抓取一個單獨的頁面,用于測試抽取效果。

>>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637

PageProcessor(頁面分析及連結抽取)

頁面分析是垂直爬蟲中需要定制的部分。在webmagic-core裡,通過實作**PageProcessor**接口來實作定制爬蟲。PageProcessor有兩個核心方法:public void process(Page page)和public Site getSite() 。

  • public void process(Page page)

    通過對**Page**對象的操作,實作爬蟲邏輯。Page對象包括兩個最重要的方法:addTargetRequests()可以添加URL到待抓取隊列,put()可以将結果儲存供後續處理。 Page的資料可以通過Page.getHtml()和Page.getUrl()擷取。

  • public Site getSite()

    Site對象定義了爬蟲的域名、起始位址、抓取間隔、編碼等資訊。

Selector是webmagic為了簡化頁面抽取開發的獨立子產品,是webmagic-core的主要着力點。這裡整合了CSS Selector、XPath和正規表達式,并可以進行鍊式的抽取。

<!-- lang: java -->
//content是用别的爬蟲工具抽取到的正文
List<String> links = page.getHtml()
.$("div.title")  //css 選擇,Java裡雖然很少有$符号出現,不過貌似$作為方法名是合法的
.xpath("//@href")  //提取連結
.regex(".*blog.*") //正則比對過濾
.all(); //轉換為string清單
           

webmagic包括一個對于頁面正文的自動抽取的類**SmartContentSelector**。相信用過Evernote Clearly都會對其自動抽取正文的技術印象深刻。這個技術又叫**Readability**。當然webmagic對Readability的實作還比較粗略,但是仍有一些學習價值。

基于Saxon,webmagic提供了XPath2.0文法的支援。XPath2.0文法支援内部函數、邏輯控制等,是一門完整的語言,如果你熟悉XPath2.0文法,倒是不妨一試(需要引入**webmagic-saxon**包)。

webmagic-samples包裡有一些為某個站點定制的PageProcessor,供學習之用。

Downloader(頁面下載下傳)

Downloader是webmagic中下載下傳頁面的接口,主要方法:

  • public Page download(Request request, Task task)

    Request對象封裝了待抓取的URL及其他資訊,而Page則包含了頁面下載下傳後的Html及其他資訊。Task是一個包裝了任務對應的Site資訊的抽象接口。

  • public void setThread(int thread)

    因為Downloader一般會涉及連接配接池等功能,而這些功能與多線程密切相關,是以定義了此方法。

目前有幾個Downloader的實作:

  • HttpClientDownloader

    內建了**Apache HttpClient**的Downloader。Apache HttpClient(4.0後整合到HttpCompenent項目中)是強大的Java http下載下傳器,它支援自定義HTTP頭(對于爬蟲比較有用的就是User-agent、cookie等)、自動redirect、連接配接複用、cookie保留、設定代理等諸多強大的功能。

  • SeleniumDownloader

    對于一些Javascript動态加載的網頁,僅僅使用http模拟下載下傳工具,并不能取到頁面的内容。這方面的思路有兩種:一種是抽絲剝繭,分析js的邏輯,再用爬蟲去重制它;另一種就是:内置一個浏覽器,直接擷取最後加載完的頁面。**webmagic-selenium**包中整合了Selenium到SeleniumDownloader,可以直接進行動态加載頁面的抓取。使用selenium需要安裝一些native的工具,具體步驟可以參考作者的博文使用Selenium來抓取動态加載的頁面

Scheduler(URL管理)

Scheduler是webmagic的管理子產品,通過實作Scheduler可以定制自己的URL管理器。Scheduler包括兩個主要方法:

  • public void push(Request request,Task task)

    将待抓取URL加入Scheduler。Request對象是對URL的一個封裝,還包括優先級、以及一個供存儲資料的Map。Task仍然用于區分不同任務,在多個任務公用一個Scheduler時可以此進行區分。

  • public Request poll(Task task)

    從Scheduler裡取出一條請求,并進行後續執行。

webmagic目前有三個Scheduler的實作:

  • QueueScheduler

    一個簡單的記憶體隊列,速度較快,并且是線程安全的。

  • FileCacheQueueScheduler

    使用檔案儲存隊列,它可以用于耗時較長的下載下傳任務,在任務中途停止後(手動停止或者程式崩潰),下次執行仍然從中止的URL開始繼續爬取。

  • RedisScheduler

    使用redis存儲URL隊列。通過使用同一台redis伺服器存儲URL,webmagic可以很容易的在多機部署,進而達到分布式爬蟲的效果。

Pipeline(後續處理和持久化)

Pipeline是最終抽取結果進行輸出和持久化的接口。它隻包括一個方法:

  • public void process(ResultItems resultItems,Task task)

    ResultItems是內建了抽取結果的對象。通過ResultItems.get(key)可以擷取抽取結果。Task同樣是用于區分不同任務的對象。

webmagic包括以下幾個Pipeline的實作:

  • ConsolePipeline

    直接輸出結果到控制台,測試時使用。

  • FilePipeline

    輸出結果到檔案,每個URL單獨儲存到一個頁面,以URL的MD5結果作為檔案名。通過構造函數

    public FilePipeline(String path)

    定義存儲路徑,**以下使用檔案持久化的類,多數都使用此方法指定路徑**。
  • JsonFilePipeline

    以JSON輸出結果到檔案(.json字尾),其他與FilePipeline相同。

webmagic目前不支援持久化到資料庫,但是結合其他工具,持久化到資料庫也是很容易的。這裡不妨看一下webmagic結合JFinal持久化到資料庫的一段代碼。因為JFinal目前還不支援maven,是以這段代碼并沒有放到webmagic-samples裡來。

webmagic-extension

webmagic-extension是為了開發爬蟲更友善而實作的一些功能子產品。這些功能完全基于webmagic-core的架構,包括注解形式編寫爬蟲、分頁、分布式等功能。

注解子產品

webmagic-extension包括注解子產品。為什麼會有注解方式?

因為PageProcessor的方式靈活、強大,但是沒有解決兩個問題:

  • 對于一個站點,如果想抓取多種格式的URL,那麼必須在PageProcesser中寫判斷邏輯,代碼難以管理。
  • 抓取結果沒有對應Model,并不符合Java程式開發習慣,與一些架構也無法很好整合。

注解的核心是Model類,本身是一個POJO,這個Model類用于傳遞、儲存頁面最終抓取結果資料。注解方式直接将抽取與資料綁定,以便于編寫和維護。

注解方式其實也是通過一個PageProcessor的實作--ModelPageProcessor完成,是以對webmagic-core代碼沒有任何影響。仍然以抓取OschinaBlog的程式為例:

@TargetUrl("http://my.oschina.net/flashsword/blog/\\d+")
public class OschinaBlog {

    @ExtractBy("//title")
    private String title;

    @ExtractBy(value = "div.BlogContent",type = ExtractBy.Type.Css)
    private String content;

    @ExtractBy(value = "//div[@class='BlogTags']/a/text()", multi = true)
    private List<String> tags;

    public static void main(String[] args) {
        OOSpider.create(
            Site.me().addStartUrl("http://my.oschina.net/flashsword/blog"),
            new ConsolePageModelPipeline(), OschinaBlog.class).run();
    }
}
           

注解部分包括以下内容:

  • TargetUrl

    "TargetUrl"表示這個Model對應要抓取的URL,它包含兩層意思:符合這個條件的URL會被加入抓取隊列;符合這個條件的URL會被這個Model抓取。TargetUrl可以**sourceRegion**指定提取URL的區域(僅支援XPath)。

    TargetUrl使用了正規表達式,比對 "http://my.oschina.net/flashsword/blog/150039" 格式的URL。webmagic對正規表達式進行了修改,"."僅表示字元"."而不代表任意字元,而"*"則代表了".*",例如"http://*.oschina.net/*"代表了oschina所有的二級域名下的URL。

    與TargetUrl相似的還有**HelpUrl**,HelpUrl表示:僅僅抓取該URL用作連結提取,并不對它進行内容抽取。例如部落格正文頁對應TargetUrl,而清單頁則對應HelpUrl。

  • ExtractBy

    • 用于字段

      "ExtractBy"可用于類以及字段。用于字段時,定義了字段抽取的規則。抽取的規則預設使用XPath,也可以選擇使用CSS Selector、正規表達式(通過設定type)。

      ExtractBy還有幾個擴充屬性。**multi**表示是否抽取清單,當然,設定為multi時,你需要一個List字段去容納它。**notnull**則表示,此字段不允許為null,若為null則放棄整個對象。

    • 用于類

      "ExtractBy"用于類時,則限定了字段抽取的區域。用于類時仍支援multi,multi則表示一個頁面可以抽取到多個對象。
    • ExtractByRaw & ExtractByUrl

      在類使用"ExtractBy"修飾後,字段的"ExtractBy"使用的是其抽取的結果,如果仍然想要抽取原HTML,可以使用"ExtractByRaw"。與此類似的還有"ExtractByUrl",表示從URL中抽取資訊。ExtractByUrl隻支援正規表達式。
    • ExtractBy2 ExtractBy3

      "ExtractBy"、"ExtractByRaw"支援鍊式抽取,通過增加注解"ExtractBy2"、"ExtractBy3"實作。
  • AfterExtractor

    AfterExtractor接口是對注解方式抽取能力不足的補充。實作AfterExtractor接口後,會在**使用注解方式填充完字段後**調用**afterProcess()**方法,在這個方法中可以直接通路已抽取的字段、補充需要抽取的字段,甚至做一些簡單的輸出和持久化操作(并不是很建議這麼做)。這部分可以參考webmagic結合JFinal持久化到資料庫的一段代碼。
  • OOSpider

    OOSpider是注解式爬蟲的入口,這裡調用**create()**方法将OschinaBlog這個類加入到爬蟲的抽取中,這裡是可以傳入多個類的,例如:
    OOSpider.create(
        Site.me().addStartUrl("http://www.oschina.net"),
        new ConsolePageModelPipeline(),
        OschinaBlog.clas,OschinaAnswer.class).run();
               
    OOSpider會根據TargetUrl調用不同的Model進行解析。
  • PageModelPipeline

    <<<<<<< HEAD

    可以通過定義PageModelPipeline來選擇結果輸出方式。這裡new ConsolePageModelPipeline()是PageModelPipeline的一個實作,會将結果輸出到控制台。 PageModelPipeline還有一個實作**JsonFilePageModelPipeline**,可以将對象持久化以JSON格式輸出,并持久化到檔案。JsonFilePageModelPipeline預設使用對象的MD5值作為檔案名,你可以在Model中實作HasKey接口,指定輸出的檔案名。

    =======

    可以通過定義PageModelPipeline來選擇結果輸出方式。這裡new ConsolePageModelPipeline()是PageModelPipeline的一個實作,會将結果輸出到控制台。

    >>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637

  • 分頁

    處理單項資料分頁(例如單條新聞多個頁面)是爬蟲一個比較頭疼的問題。webmagic目前對于分頁的解決方案是:在注解模式下,Model通過實作**PagedModel**接口,并引入PagedPipeline作為第一個Pipeline來實作。具體可以參考webmagic-samples中抓取網易新聞的代碼:**us.codecraft.webmagic.model.samples.News163**。

    關于分頁,這裡有一篇對于webmagic分頁實作的詳細說明的文章關于爬蟲實作分頁的一些思考。 <<<<<<< HEAD 目前分頁功能還沒有分布式實作,如果使用RedisScheduler進行分布式爬取,請不要使用分頁功能。

    ======= 目前分頁功能還沒有分布式實作,如果實作RedisScheduler進行分布式爬取,請不要使用分頁功能。

    >>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637

分布式

webmagic-extension中,通過redis來管理URL,達到分布式的效果。但是對于分布式爬蟲,僅僅程式能夠分布式運作,還滿足不了大規模抓取的需要,webmagic可能後期會加入一些任務管理和監控的功能,也歡迎各位使用者為webmagic送出代碼,做出貢獻。

<<<<<<< HEAD

更進一步

如果這篇文檔仍然滿足不了你的需要,你可以閱讀webmagic的Javadoc,或者直接閱讀源碼。

======= >>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637