NCrawler:.NET 的 CrawlerT

Codeplex 軟體套件(Package)資訊
套件名稱NCrawler
作者Developers: boomhauer
Coordinators: EsbenCarlsen
目前版本2.0 Stable
URL http://ncrawler.codeplex.com/
使用難易度
使用此套件時可用的輔助工具
基礎知識基本類別使用。
知曉 Regular Expression。
熟悉介面的使用。

偵測資訊:網路爬蟲 Crawler

搜尋引擎如 Google/Bing 或是 Yahoo 等等,廣納了全球數百萬(甚至數千萬)個網站的網址,以及以海量來計的網頁連結與內容,以作為廣大網路使用搜尋資料之用,廣告商也由此處獲得商機,在搜尋引擎的各個結果頁部份安插廣告以增加收益,不過你可知道搜尋引擎資料庫的這些網頁與網址資訊是如何來的嗎?答案就是數千甚至數萬個執行緒(thread)的網路爬蟲(Crawler)。

網路爬蟲是機器人(bot)的一種,它由搜尋引擎的網址資料庫取得根網址的資料(一般使用網站註冊時所輸入的資料,或由其他網址的相關連結取得)後,自身即開始產生數個(或數十個,由管理人員設定)執行緒,針對網址所包含的網頁(通常是首頁)中的各個連結開始偵測,連結可能是一個網頁,也可以是一個圖檔、文件、視訊、甚至於是 JavaScript 指令碼等等,下載到主程式中,主程式會再剖析內容以找出連結資料,然後再繼續往更深的層次鑽探,所有下載剖析得到的 URL 都會被送入一個佇列(Queue)中(不論是由哪支程式探得的),等待由前一個處理完的執行緒取得新的 URL 再進行處理,直到最後一個 URL 被處理完畢為止。最後將所得的資料儲存到資料庫中,以備搜尋引擎使用。


圖:標準的 Web Crawler 的內部高階架構(來源:維基百科)

 

NOTE

通常在公眾網站伺服器的記錄檔(例如 IIS 的 log 檔)中,或多或少都會看到一些 Bot 的來訪記錄,這些都是 Web Crawler 進入網站時留下的,多數 Crawler 都會註明來源(例如 GoogleBot),且通常只有在公眾網路會出現,如果私人網路上有出現,那可能表示防火牆有設定問題(因為私人網路通常不會讓 Bot 進來的,或是內部也有類似的軟體會使用 Crawler)。

 

不過除了搜尋引擎以外,企業搜尋(Enterprise Search)以及知識管理(KM)類型的系統,或多或少都會用到 Crawler 的能力,主要原因是這些系統都需要處理到大量的資料,而這些資料的來源都要依賴 Crawler 的能力,像是 Database Crawler 或是 Document Crawler 等等的工具,它們可以深入不同的資料來源去找出需要的資料在哪個位置,並且運用關鍵字或標記將它們的位置記錄下來後,使用者即可直接以關鍵字去搜尋結果集,然後取得要找的資料的位置(例如檔案內容包含某個關鍵字,或是資料記錄中有某個關鍵字等)。

 

NOTE

應用在企業網路以及政府網站的整站檢索(Entire Site Search)或全文檢索(Full-Text Search)的資料來源(資料庫)基本上就是應用 Crawler 的技術在做的,而且它們都是一個套裝軟體,例如 Openfind、龍捲風搜尋或是 Microsoft Search Server 等都有 Crawler 的影子。

 

NOTE

比起 Web Crawler,企業搜尋要用的技術比搜尋引擎要來的多,除了文字搜尋以外,圖像搜尋(Image Search)、光學辨識(OCR)以及文字探勘(Text Mining)等,都是企業搜尋軟體可能會用到的技術,其原因是企業資料的類型比搜尋引擎要多太多了(各類型的檔案與內容都不太一樣)。但近幾年,搜尋引擎也開始在使用這些技術,例如 Google 的圖片搜尋( http://images.google.com.tw/imghp?hl=zh-TW&tab=wi)。

 

另外,以網站內容為主的內容管理系統(Content Management System)也會應用到 Crawler,其主要功用的探知無效連結(Bad Links/Invalid Links/Failed Links),以先行反應可能的資源中斷問題。另一種應用則是偵測網頁的熱門程度(其他網頁中有使用到該網頁的程度),這可以被視為網站評量的其中一個指數。本文所要介紹的 NCrawler,就是可以快速開發 Web Crawler 的一個工具。

NCrawler 簡介

NCrawler 是一個 Web Crawler 工具,它可以讓開發人員很輕鬆的發展出具有 Web Crawler 能力的應用程式,並且具有可以延展的能力,讓開發人員可以擴充它的功能,以支援其他類型的資源(例如 PDF/Word/Excel 等檔案或其他資料來源)。NCrawler 使用多執行緒(Multi-threading)模式針對網路資源進行探索,並且會依照給定的步驟來處理抓取到的資源,然後依給定的資源來活動(像是寫入資料庫或是擷取部份資料等等)。

目前 NCrawler 支援的搜尋類型有:

  • HTML 網頁(需要 HtmlAgilityPack.dll)。
  • PDF 檔案(需要 iTextSharp PDF Library)。

而 NCrawler 支援的中介儲存區有:

  • 記憶體(使用 NCrawler.Crawler 進行時)。
  • 資料庫(使用 NCrawler.DbServices.Crawler 進行時)。
  • 隔離儲存區(使用 NCrawler.IsolatedStorageServices.Crawler 進行時)。

NCrawler 的中介儲存區儲存了包含網址以及探索佇列等資料,以供應 NCrawler 引擎擷取網址以進行作業之用。並且保留歷史資料以備查詢。

例如,如果要搜尋某個網站的所有連結,可以使用下列的程式碼:

[C#]
Crawler c = new Crawler("http://mysite.com.tw", new HtmlDocumentProcessor())
{
    MaximumThreadCount = 5,
    MaximumCrawlDepth = 2
};

c.Crawl();

上列程式碼是不會輸出任何東西,因為輸出也是一個步驟(Step),如果要輸出的話,需要另外寫一個繼承自 IPipelineStep 介面的類別,處理它的 Process() 方法以輸出資料:

[C#]
public class DumpResult : IPipelineStep
{
    public void Process(Crawler crawler, PropertyBag propertyBag)
    {
        Console.WriteLine("Find URL:" + propertyBag.Step.Uri);
    }
}

然後改寫上面的程式,將 DumpResult 類別加進去:

[C#]
Crawler c = new Crawler("http://mysite.com.tw", new HtmlDocumentProcessor(), new DumpResult())
{
    MaximumThreadCount = 5,
    MaximumCrawlDepth = 2
};

c.Crawl();

這樣就可以透過 DumpResult 類別取得所有被探知到的連結路徑。

使用方式

首先,先下載原始檔並解壓縮後,使用 Visual Studio 2008 開啟 NCrawler.sln 檔案,並選擇功能表的『建置/建置方案』,重新編譯所有程式,編譯完成後,就可以在各專案資料夾下的 bin\Debug(或 bin\Release,看是用哪種模式編譯)下找到元件的 DLL 檔案,在需要使用它的專案中引用它們的參考即可。若元件需要使用到其他的 DLL(如前面列表中所示),則必要的 DLL 可以在根目錄的 Repository 資料夾中找到。

Crawler 是 NCrawler 的主類別,啟動網路探索的機制要由它來做,它主控了下載資源回用戶端後的解析程序,以及管理啟動的執行緒,開發人員可以透過設定 MaximumThreadCount 的值來設定最大可同時啟動多少執行緒來處理;同時為了要控制探索的深度以避免無限迴圈探索(infinite loop discover)的問題,它提供了 MaximumCrawlDepth 屬性以設定最大的深度;它也可以設定要排除哪些資源,例如不針對圖檔進行內容探索或不抓取圖檔路徑時,能利用 DisAllowedUrls 屬性來排除不允許的 URL 集合。

[C#]
Crawler c = new Crawler("http://ncrawler.codeplex.com/", new HtmlDocumentProcessor(), new DumperStep());
c.MaximumThreadCount = 3;
c.MaximumCrawlDepth = 2;
c.BlackListedUriRegexMatchers = new [] { new Regex(@"(\.jpg|\.css|\.js|\.gif|\.jpeg|\.png)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase) };
c.Crawl();

NOTE

在 NCrawler 官方網站上的範例,屬性名稱是舊版的,在新版會找不到屬性,因此若需要範例,可以參考新版的 NCrawler.Demo 專案中的範例程式。

 

實務應用:無效連結偵測程式

NCrawler 具有強大的 URL 探索能力,以及多執行緒的處理能力,因此筆者使用它撰寫一支簡單的無效連結偵測程式(只要不是 HTTP 200 的回應都算無效),可以使用 Windows 排程來定時執行,並回報無效連結的偵測結果,列出無效連結的清單報表等。無效連結偵測程式由 InvalidLinkDetector 類別作為核心功能提供者,裡面包裝了 NCrawler 以及記錄用的 XML Document 物件,程式碼如下:

[C#]
public class InvalidLinkDetector
{
    private XmlDocument _writerDoc = null;
    private Crawler _crawler = null;

    public InvalidLinkDetector(string DetectUrl, int MaxThreadCount, int MaxCrawlDepth)
    {
        this._writerDoc = new XmlDocument();
        this._writerDoc.LoadXml("<invalidUrls></invalidUrls>");

        this._crawler = new Crawler(DetectUrl, new HtmlDocumentProcessor(), new ScanResultWriter(this._writerDoc))
        {
            MaximumThreadCount = MaxThreadCount,
            MaximumCrawlDepth = MaxCrawlDepth
        };

        this._crawler.CrawlFinished += new EventHandler<NCrawler.Events.CrawlFinishedEventArgs>(CrawlFinished);
        this._crawler.PipelineException += new EventHandler<NCrawler.Events.PipelineExceptionEventArgs>(PipelineException);
    }

    public void Run()
    {
        this._crawler.Crawl();
    }

    private void PipelineException(object sender, NCrawler.Events.PipelineExceptionEventArgs e)
    {
        Console.WriteLine("Exception occurred in pipeline: {0}, message: {1}", e.PropertyBag.Step.GetType().Name, e.Exception.Message);
    }

    private void CrawlFinished(object sender, NCrawler.Events.CrawlFinishedEventArgs e)
    {
        this._writerDoc.Save(Environment.CurrentDirectory + @"\" + DateTime.Now.ToString("yyyyMMdd HHmmss") + ".xml");
        this._writerDoc = null;
    }
}

public class ScanResultWriter : IPipelineStep
{
    private XmlDocument _writerDoc = null;

    public ScanResultWriter(XmlDocument doc)
    {
        this._writerDoc = doc;
    }

    public void Process(Crawler crawler, PropertyBag propertyBag)
    {
        if (propertyBag.StatusCode != HttpStatusCode.OK)
        {
            Console.WriteLine("Find a invalid link...");

            XmlNode node = this._writerDoc.CreateNode(XmlNodeType.Element, "invalidUrl", null);
            XmlNode nodeUrl = this._writerDoc.CreateNode(XmlNodeType.Attribute, "url", null);
            XmlNode nodeReferUrl = this._writerDoc.CreateNode(XmlNodeType.Attribute, "refer", null);
            XmlNode nodeReason = this._writerDoc.CreateNode(XmlNodeType.Attribute, "reason", null);

            nodeUrl.Value = propertyBag.OriginalUrl;
            nodeReferUrl.Value = propertyBag.OriginalReferrerUrl;
            nodeReason.Value = ((int)propertyBag.StatusCode).ToString();

            node.Attributes.SetNamedItem(nodeUrl);
            node.Attributes.SetNamedItem(nodeReferUrl);
            node.Attributes.SetNamedItem(nodeReason);

            this._writerDoc.DocumentElement.AppendChild(node);
        }
    }
}

而外部程式只要如此呼叫即可:

[C#]
static void Main(string[] args)
{
    Console.WriteLine("Scanning...");
    InvalidLinkDetector detector = new InvalidLinkDetector("http://msdn.microsoft.com", 10, 4);
    detector.Run();
}

這支程式的執行步驟有兩個,一個是剖析 HTML 的 HtmlDocumentProcessor,它會分析下載的 HTML 資源,並且取出裡面的連結,而 ScanResultWriter 則是會利用 Crawler 傳入的 PropertyBag 事件參數來判斷是否該要求是 HTTP 404,如果是的話會寫入資料到 XmlDocument,並且在 Crawler 結束執行時(CrawlFinished 事件引發)儲存到磁碟中。

 

NOTE

任何要作為 Crawler 執行步驟的物件,都必須繼承自 IPipelineStep 介面,才可以被 Crawler 取用。

 

此程式執行的畫面如下:

而偵測到無效連結時的回報文件格式如下:

[XML]
<invalidUrls>
  <invalidUrl url="/vs2008/products/cc268305.aspx" refer="http://msdn.microsoft.com/en-us/default.aspx" reason="404" />
</invalidUrls>
  • url 是偵測的網址。
  • refer 是該網址的來源網址。
  • reason 是原因,以代碼表示,例如 404 是找不到網頁。

有了這件 XML 文件,讀者可自行設計套用它的 XSLT 轉換格式檔,讓它可以呈現具親和力的報表樣式。