Hawk-數據抓取工具:簡明教程
Hawk: Advanced Crawler& ETL tool written in C#/WPF
1.軟件介紹
HAWK是一種數據采集和清洗工具,依據GPL協議開源,能夠靈活,有效地采集來自網頁,數據庫,文件, 並通過可視化地拖拽,
快速地進行生成,過濾,轉換等操作。其功能最適合的領域,是爬蟲和數據清洗。
Hawk的含義為“鷹”,能夠高效,准確地捕殺獵物。
HAWK使用C# 編寫,其前端界面使用WPF開發,支持插件擴展。通過圖形化操作,能夠快速建立解決方案。
GitHub地址:https://github.com/ferventdesert/Hawk
其Python等價的實現是etlpy:
http://www.cnblogs.com/buptzym/p/5320552.html
筆者專門為其開發的工程文件已公開在GitHub:
https://github.com/ferventdesert/Hawk-Projects
使用時,點擊文件,加載工程即可加載。
不想編譯的話,可執行文件在:
https://github.com/ferventdesert/Hawk/tree/master/Versions
編譯路徑在:
Hawk.Core\Hawk.Core.sln
以獲取大眾點評的所有北京美食為例,使用本軟件可在10分鍾內完成配置,在1小時之內自動並行抓取全部內容,並能監視子線程工作情況。而手工編寫代碼,即使是使用python,一個熟練的程序員也可能需要一天以上:
視頻演示,復雜度由小到大:
2.界面和組件介紹
2.1 界面介紹
軟件采用類似Visual Studio和Eclipse的Dock風格,所有的組件都可以懸停和切換。包括以下核心組件:
- 左上角區域:主要工作區,可模塊管理。
- 下方: 輸出調試信息,和任務管理,監控一項任務完成的百分比。
- 右上方區域: 屬性管理器,能對不同的模塊設置屬性。
- 右下方區域: 顯示當前已經加載的所有數據表和模塊。
2.2 數據管理
能夠添加來自不同數據源的連接器, 並對數據進行加載和管理:
在空白處,點擊右鍵,可增加新的連接器。在連接器的數據表上,雙擊可查看樣例,點擊右鍵,可以將數據加載到內存中。也可以選擇加載虛擬數據集,此時系統會維護一個虛擬集合, 當上層請求分頁數據時, 動態地訪問數據庫, 從而有效提升性能。
2.3 模塊管理
目前系統僅僅提供了兩個模塊: 網頁采集器和數據清洗ETL, 雙擊即可加載一個新的模塊。
之前配置好的模塊,可以保存為任務, 雙擊可加載一個已有任務:
2.4 系統狀態管理
當加載了數據集或模塊時,在系統狀態管理中,就可對其查看和編輯:
點擊右鍵,可以對數據集進行刪除,修改名稱等。也可以將數據集拖拽到下方的圖標上,如拖到回收站,即可刪除該模塊。
雙擊數據集或模塊,可查看模塊的內容。 將數據集拖拽到數據清洗( 數據視圖的下方第一個圖標),可直接對本數據集做數據清洗。
3.網頁采集器
3.1 原理(建議閱讀)
網頁采集器的功能是獲取網頁中的數據(廢話)。通常來說,目標可能是列表(如購物車列表),或是一個頁面中的固定字段(如JD某商品的價格和介紹,在頁面中只有一個)。因此需要設置其讀取模式。傳統的采集器需要編寫正則表達式,但方法過分復雜。如果認識到html是一棵樹,只要找到了承載數據的節點即可。XPath就是一種在樹中描述路徑的語法。指定XPath,就能搜索到樹中的節點。
手工編寫XPath也很復雜,因此軟件可以通過關鍵字,自動檢索XPath,提供關鍵字,軟件就會從樹中遞歸搜索包含該數據的葉子節點。因此關鍵字最好是在頁面中獨一無二的。
如上圖所示,只要提供“北京”和“42”這兩個關鍵字,就能找到parent節點, 進而獲取div[0]和div1這兩個列表元素。通過div[0]和div1兩個節點的比較,我們就能自動發現相同的子節點(name,mount)和不同的節點(北京:上海,37:42)。相同的節點會保存為屬性名,不同的節點為屬性值。但是,不能提供北京和37,此時,公共節點是div[0], 這不是列表。
軟件在不提供關鍵字的情況下,也能通過html文檔的特征,去計算最可能是列表父節點(如圖中的parent)的節點,但當網頁特別復雜時,猜測可能會出錯,所以需要至少提供兩個關鍵字( 屬性)。
本算法原理是原創的,可查看源碼或留言交流。
3.2 基本列表
我們以爬取鏈家二手房為例,介紹網頁采集器的使用。首先雙擊圖標,加載采集器:
在最上方的地址欄中,輸入要采集的目標網址,本次是http://bj.lianjia.com/ershoufang/。並點擊刷新網頁。此時,下方展示的是獲取的html文本。原始網站頁面如下:
由於軟件不知道到底要獲取哪些內容,因此需要手工給定幾個關鍵字, 讓Hawk搜索關鍵字, 並獲取位置。
以上述頁面為例,通過檢索820萬和51789(單價,每次采集時都會有所不同),我們就能通過DOM樹的路徑,找出整個房源列表的根節點。
下面是實際步驟
由於要抓取列表,所以讀取模式選擇List。 填入搜索字符700, 發現能夠成功獲取XPath, 編寫屬性為“總價” ,點擊添加字段,即可添加一個屬性。類似地,再填入30535,設置屬性名稱為“單價”,即可添加另外一個屬性。
如果發現有錯誤,可點擊編輯集合, 對屬性進行刪除,修改和排序。
你可以類似的將所有要抓取的特征字段添加進去,或是直接點擊手氣不錯,系統會根據目前的屬性,推測其他屬性:
屬性的名稱是自動推斷的,如果不滿意,可以修改列表第一列的屬性名, 在對應的列中敲鍵盤回車提交修改。之后系統就會自動將這些屬性添加到屬性列表中。
工作過程中,可點擊提取測試 ,隨時查看采集器目前的能夠抓取的數據內容。這樣,一個鏈家二手房的網頁采集器即可完成。可屬性管理器的上方,可以修改采集器的模塊名稱,這樣就方便數據清洗 模塊調用該采集器。
4. 數據清洗
數據清洗模塊,包括幾十個子模塊, 這些子模塊包含四類:生成, 轉換, 過濾和執行
4.0 原理(可跳過)
4.0.1 C#版本的解釋
數據清洗的本質是動態組裝Linq,其數據鏈為IEnumerable<IFreeDocument>
。 IFreeDocument
是 IDictionary<string, object>
接口的擴展。 Linq的Select函數能夠對流進行變換,在本例中,就是對字典不同列的操作(增刪改),不同的模塊定義了一個完整的Linq
流:
result= source.Take(mount).where(d=>module0.func(d)).select(d=>Module1.func(d)).select(d=>Module2.func(d))….
借助於C#編譯器的恩賜, Linq能很方便地支持流式數據,即使是巨型集合(上億個元素),也能夠有效地處理。
4.0.2 Python版本的解釋
由於Python沒有Linq, 因此組裝的是生成器(generator), 對生成器進行操作,即可定義出類似Linq的完整鏈條:
for tool in tools: generator = transform(tool, generator)
詳細源代碼,可以參考Github上的開源項目https://github.com/ferventdesert/etlpy/
4.1 以鏈家為例的抓取
4.1.1構造url列表
在3.1節介紹了如何實現一個頁面的采集,但如何采集所有二手房數據呢? 這涉及到翻頁。
還是以鏈家為例,翻頁時,我們會看到頁面是這樣變換的:
http://bj.lianjia.com/ershoufang/pg2/ http://bj.lianjia.com/ershoufang/pg3/ …
因此,需要構造一串上面的url. 聰明的你肯定會想到, 應當先生成一組序列, 從1到100(假設我們只抓取前100頁)。
- 雙擊數據清洗ETL左側的搜索欄中搜索生成區間數, 將該模塊拖到右側上方的欄目中:
- 在右側欄目中雙擊生成區間數,可彈出設置窗口, 為該列起名字(id), 最大值填寫為100,生成模式默認為Append:
為什么只顯示了前20個? 這是程序的虛擬化機制, 並沒有加載全部的數據,可在ETL屬性的調試欄目中,修改采樣量(默認為20)。 - 將數字轉換為url, 熟悉C#的讀者,可以想到string.format, 或者python的%符號:搜索合並多列,並將其拖拽到剛才生成的id列, 編寫format為下圖的格式,即可將原先的數值列變換為一組url
(如果需要多個列合並為一個列, 則在“其他項” 欄目中填寫其他列的列名,用空格分割, 並在format中用{1},{2}..等表示)
(由於設計的問題,數據查看器的寬度不超過150像素,因此對長文本顯示不全,可以在右側屬性對話框點擊查看樣例, 彈出的編輯器可支持拷貝數據和修改列寬。
4.1.2 使用配置好的網頁采集器
生成這串URL之后,我們即可將剛才已經完成的網頁采集器與這串url進行合並。
拖拽從爬蟲轉換到當前的url,雙擊該模塊:將剛才的網頁采集器的名稱, 填入爬蟲選擇 欄目中。
之后,系統就會轉換出爬取的前20條數據:
可以看到, 數據中“屬性3” 列包含html轉義符, 拖拽html字符轉義,到屬性3列,即可自動轉換所有轉義符。
如果要修改列名,在最上方的列名上直接修改, 點擊回車即可修改名字。
where(面積)列中包含數字,想把數字提取出來,可以將提取數字模塊拖拽到該列上,所有數字即可提取出來。
類似地,可以拖拽字符分割或正則分割 到某一列,從而分割文本和替換文本。此處不再贅述。
有一些列為空,可以拖拽空對象過濾器 到該列,那么該列為空的話會自動過濾這一行數據。
4.1.4 保存和導出數據
需要保存數據時,可以選擇寫入文件,或者是臨時存儲(本軟件的數據管理器),或是數據庫。因此可以將“執行” 模塊, 拖入清洗鏈的后端:
拖寫入數據表到任意一列, 並填入新建表名(如鏈家二手房)
下圖是這次操作的所有子模塊列表:
之后,即可對整個過程進行操作:
選擇串行模式或並行模式, 並行模式使用線程池, 可設定最多同時執行的線程數(最好不要超過100)。推薦使用並行模式,
點擊執行按鈕,即可在任務管理視圖中采集數據。
之后,在數據管理的數據表鏈家二手房上點擊右鍵, 選擇另存為, 導出到Excel,Json等,即可將原始數據導出到外部文件中。
類似的, 你可以在清洗流程中拖入執行器,則保存的是中間過程,也可以在結尾拖入多個執行器,這樣就能同時寫入數據庫或文件, 從而獲得了極大的靈活性。
4.1.5 保存任務
在右下角的算法視圖中的任意模塊上點擊右鍵,保存任務,即可在任務視圖中保存新任務(任務名稱與當前模塊名字一致),下次可直接加載即可。如果存在同名任務, 則會對原有任務進行覆蓋。
在算法視圖的空白處,點擊保存所有模塊,會批量保存所有的任務。
你可以將一批任務,保存為一個工程文件(xml),並在之后將其加載和分發。
5.總結
上文以抓取房地產網站鏈家為例,介紹了軟件的整體使用流程。當然,Hawk功能遠遠不限於這些。之后我們將通過一系列文章來介紹其使用方法。
值得提醒的是,由於軟件功能在不斷地升級,可能會出現視頻和圖片與軟件中不一致的情況,因此所有的介紹和功能以軟件的實際功能為准。