2013年9月17日 星期二

[轉貼] 網路爬蟲 .NET 的 CrawlerT


NCrawler:.NET 的 CrawlerT

Codeplex 軟體套件(Package)資訊
套件名稱NCrawler
作者Developers: boomhauer
Coordinators: EsbenCarlsen
目前版本2.0 Stable
URLhttp://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#]
  1. Crawler c = new Crawler("http://mysite.com.tw"new HtmlDocumentProcessor())
  2. {
  3.     MaximumThreadCount = 5,
  4.     MaximumCrawlDepth = 2
  5. };
  6.  
  7. c.Crawl();
上列程式碼是不會輸出任何東西,因為輸出也是一個步驟(Step),如果要輸出的話,需要另外寫一個繼承自 IPipelineStep 介面的類別,處理它的 Process() 方法以輸出資料:
[C#]
  1. public class DumpResult : IPipelineStep
  2. {
  3.     public void Process(Crawler crawler, PropertyBag propertyBag)
  4.     {
  5.         Console.WriteLine("Find URL:" + propertyBag.Step.Uri);
  6.     }
  7. }
然後改寫上面的程式,將 DumpResult 類別加進去:
[C#]
  1. Crawler c = new Crawler("http://mysite.com.tw"new HtmlDocumentProcessor(), new DumpResult())
  2. {
  3.     MaximumThreadCount = 5,
  4.     MaximumCrawlDepth = 2
  5. };
  6.  
  7. 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#]
  1. Crawler c = new Crawler("http://ncrawler.codeplex.com/"new HtmlDocumentProcessor(), new DumperStep());
  2. c.MaximumThreadCount = 3;
  3. c.MaximumCrawlDepth = 2;
  4. c.BlackListedUriRegexMatchers = new [] { new Regex(@"(\.jpg|\.css|\.js|\.gif|\.jpeg|\.png)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase) };
  5. c.Crawl();
NOTE
在 NCrawler 官方網站上的範例,屬性名稱是舊版的,在新版會找不到屬性,因此若需要範例,可以參考新版的 NCrawler.Demo 專案中的範例程式。

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

NCrawler 具有強大的 URL 探索能力,以及多執行緒的處理能力,因此筆者使用它撰寫一支簡單的無效連結偵測程式(只要不是 HTTP 200 的回應都算無效),可以使用 Windows 排程來定時執行,並回報無效連結的偵測結果,列出無效連結的清單報表等。無效連結偵測程式由 InvalidLinkDetector 類別作為核心功能提供者,裡面包裝了 NCrawler 以及記錄用的 XML Document 物件,程式碼如下:
[C#]
  1. public class InvalidLinkDetector
  2. {
  3.     private XmlDocument _writerDoc = null;
  4.     private Crawler _crawler = null;
  5.  
  6.     public InvalidLinkDetector(string DetectUrl, int MaxThreadCount, int MaxCrawlDepth)
  7.     {
  8.         this._writerDoc = new XmlDocument();
  9.         this._writerDoc.LoadXml(" ");
  10.  
  11.         this._crawler = new Crawler(DetectUrl, new HtmlDocumentProcessor(), new ScanResultWriter(this._writerDoc))
  12.         {
  13.             MaximumThreadCount = MaxThreadCount,
  14.             MaximumCrawlDepth = MaxCrawlDepth
  15.         };
  16.  
  17.         this._crawler.CrawlFinished += new EventHandler (CrawlFinished);
  18.         this._crawler.PipelineException += new EventHandler (PipelineException);
  19.     }
  20.  
  21.     public void Run()
  22.     {
  23.         this._crawler.Crawl();
  24.     }
  25.  
  26.     private void PipelineException(object sender, NCrawler.Events.PipelineExceptionEventArgs e)
  27.     {
  28.         Console.WriteLine("Exception occurred in pipeline: {0}, message: {1}", e.PropertyBag.Step.GetType().Name, e.Exception.Message);
  29.     }
  30.  
  31.     private void CrawlFinished(object sender, NCrawler.Events.CrawlFinishedEventArgs e)
  32.     {
  33.         this._writerDoc.Save(Environment.CurrentDirectory + @"\" + DateTime.Now.ToString("yyyyMMdd HHmmss") + ".xml");
  34.         this._writerDoc = null;
  35.     }
  36. }
  37.  
  38. public class ScanResultWriter : IPipelineStep
  39. {
  40.     private XmlDocument _writerDoc = null;
  41.  
  42.     public ScanResultWriter(XmlDocument doc)
  43.     {
  44.         this._writerDoc = doc;
  45.     }
  46.  
  47.     public void Process(Crawler crawler, PropertyBag propertyBag)
  48.     {
  49.         if (propertyBag.StatusCode != HttpStatusCode.OK)
  50.         {
  51.             Console.WriteLine("Find a invalid link...");
  52.  
  53.             XmlNode node = this._writerDoc.CreateNode(XmlNodeType.Element, "invalidUrl"null);
  54.             XmlNode nodeUrl = this._writerDoc.CreateNode(XmlNodeType.Attribute, "url"null);
  55.             XmlNode nodeReferUrl = this._writerDoc.CreateNode(XmlNodeType.Attribute, "refer"null);
  56.             XmlNode nodeReason = this._writerDoc.CreateNode(XmlNodeType.Attribute, "reason"null);
  57.  
  58.             nodeUrl.Value = propertyBag.OriginalUrl;
  59.             nodeReferUrl.Value = propertyBag.OriginalReferrerUrl;
  60.             nodeReason.Value = ((int)propertyBag.StatusCode).ToString();
  61.  
  62.             node.Attributes.SetNamedItem(nodeUrl);
  63.             node.Attributes.SetNamedItem(nodeReferUrl);
  64.             node.Attributes.SetNamedItem(nodeReason);
  65.  
  66.             this._writerDoc.DocumentElement.AppendChild(node);
  67.         }
  68.     }
  69. }
而外部程式只要如此呼叫即可:
[C#]
  1. static void Main(string[] args)
  2. {
  3.     Console.WriteLine("Scanning...");
  4.     InvalidLinkDetector detector = new InvalidLinkDetector("http://msdn.microsoft.com"104);
  5.     detector.Run();
  6. }
這支程式的執行步驟有兩個,一個是剖析 HTML 的 HtmlDocumentProcessor,它會分析下載的 HTML 資源,並且取出裡面的連結,而 ScanResultWriter 則是會利用 Crawler 傳入的 PropertyBag 事件參數來判斷是否該要求是 HTTP 404,如果是的話會寫入資料到 XmlDocument,並且在 Crawler 結束執行時(CrawlFinished 事件引發)儲存到磁碟中。

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

此程式執行的畫面如下:
而偵測到無效連結時的回報文件格式如下:
[XML]
  1.   "/vs2008/products/cc268305.aspx" refer="http://msdn.microsoft.com/en-us/default.aspx" reason="404" />
  • url 是偵測的網址。
  • refer 是該網址的來源網址。
  • reason 是原因,以代碼表示,例如 404 是找不到網頁。
有了這件 XML 文件,讀者可自行設計套用它的 XSLT 轉換格式檔,讓它可以呈現具親和力的報表樣式。

沒有留言:

張貼留言