張 倩, 林安成, 廖秀秀
(廣東技術師范學院 計算機科學學院, 廣州 510665)
爬蟲技術的目標是對海量的分散的互聯網數據進行采集, 是搜索引擎系統的基礎. 大數據近年來發展快速, 不僅是數據容量的巨大, 更是強調對樣本數據的分析. 互聯網中的數據包含大量有價值但異構的信息, 需要被提取出來并以一種結構化的形式來表示, 作為大數據的數據來源[1,2].主題爬蟲垂直于某一領域, 相比于通用爬蟲, 增加了對有價值數據的組織和歸類. 從一些主題爬蟲的實踐發現, 他們通常手工編寫結構化代碼來分析某一個目標站點[3], 很多情況下需要獲得多個源站的結構化數據, 就不得不分別編寫相關代碼, 所付出的時間和人力代價是不小的. 而且, 爬蟲系統的編寫是在前人的經驗基礎之上, 大多是重復性的勞動, 特別是垂直于同類網站的主題爬蟲, 分別編寫規則更顯得繁瑣和沒有建設性, 那么尋找一個相對自動化的方案就顯得十分有意義. 前人對自動化爬蟲的研究還不能夠很好地適應上文提到的場景, 比如基于主題語義的信息采集, 無法做到具體字段的結構化[4]; 而使用概率模型來采集最優數據[5], 其可靠性和最終的數據量不符合實際工程應用上的要求. 為了得到一個可靠而高效的自動結構化爬蟲, 本文選擇從電商網站切入研究, 其具有一些特點: 層次分布穩定, 有價值數據比較密集, 標簽、數據變化快, 采用了一些主流反爬機制. 基于這些特點, 電商網站成為合適的樣本. 本文將從研究源站的統一性開始, 設計和采用一些抓取的方法和技巧, 實現自動結構化的目的. 同時, 為了追求效率和工程化, 補充了在實際使用的解決方案.
電商網站的層次分布是穩定的, 調研了國內幾家大型電商網站(淘寶、蘇寧、京東)和一些小型商戶使用的開源商城程序(ECSHOP、ShopN、HiShop), 為了讓用戶更容易適應頁面操作和商品入口, 這些網站一般被組織為如圖1所示的結構.

圖1 電商網站組織結構
對用戶來說, 可通過商品分類和搜索匹配兩種方式進入列表頁面, 列表頁面即是眾多商品的入口, 也是信息的主要通道. 爬蟲系統采用的路線是“分類-列表-詳情”, 原因有三點: 1) 商品分類本身也是有價值數據;2) 分類和商品的關聯性更是隱式的重要信息; 3) 從這條路線走下來, 理論上可以抓取整站而不遺漏, 具有確定性.
確定網站結構的層級是很有意義的, 它可以確定當前抓取和下一層抓取的是哪種頁面類型, 這樣就可以給不同類型做抓取策略上的匹配, 而不用通過語義層面來標識[6], 降低了系統復雜度, 提升了效率和精準度.
頁面有靜態和動態之分, 這影響了爬蟲系統的抓取行為:
(1) 大多數情況下, 每個url對應的是靜態頁面, 瀏覽器直接解析請求url后響應的html.
(2) 動態頁面的情況是比較特殊的, 網頁的數據采用異步加載, 即站點服務器初次響應的數據僅僅是頁面結構框架和異步執行的代碼, 加載完畢后, 再次請求服務器拿到數據, 通過JavaScript操作Dom組合成完整頁面.
特別在近年來, 前端應用引擎諸如React、AngularJS和國產的vue日益盛行, 模板化和異步加載的方式更是應用廣泛, 導致上面所述第二種情況出現的幾率不低. 所幸爬蟲領域也有足夠多的方案去適配它, 有人通過模擬它的JS行為獲得數據[7], 更通用的方法是應用各平臺的前端渲染支持庫(HtmlUtil、PhantomJS), 它們帶有JS引擎, 就好像真正在瀏覽器加載頁面等待渲染完畢一樣, 有人應用這些支持庫實現了比較優秀的系統[8], 甚至形成了稱為AjaxCrawler的體系[9].
電商網站也是一樣, 兩種頁面經常是共存的, 為了提高普適度, 本文對動態頁面采取了應用前端渲染支持庫的做法. 那么如何標識出當前頁面是屬于哪一種類型呢?本文提出一種“預抓取-比較判斷-標識”的方法, 其頁面類型判斷流程如圖2所示.
圖2中的“比較”過程參考了一種基于網頁正文結構和特征串的相似網頁去重算法[10], 如圖3所示.
首先進行網頁正文的抽取, 過濾掉網頁中的噪聲(例如導航條、廣告條、超鏈接和網站底部說明), 利用網頁正文生成樹算法得到一棵結構樹, 然后用Bloom Filter算法計算每一層次標簽特征串的指紋, 用頁面所有元素的指紋值直接拼接進行網頁相似度的判斷. 當相似度達到預定的閾值, 就認為靜態抓取和前端渲染得到的頁面數據是等價的, 此時可以斷定該層級頁面是靜態的, 否則認為是存在異步獲取過程的動態頁面.由于經歷了正文抽取、噪聲過濾和文本樹生成, 所有節點的指紋都是相對穩定的, 這就意味著兩個頁面的指紋相似度應該稍高一些, 本文測試環境使用的閾值為0.8.

圖2 頁面類型判斷流程

圖3 相似頁面判斷算法
電商網站層級嚴謹, 基于此相同層級的頁面, 自然采取了同樣的頁面類型, 所以圖2最后一步中標記的是某一層級而不是某個URL, 后面的抓取都沿用本層級已經確定下來的策略即可.
主題爬蟲的特點除了上文所述穩定的層次分布,更重要的是, 其垂直領域的語料和高頻度用詞都是可預見的, 而在技術角度, 描述不同業務數據的標識符也是有差異的. 依舊以電商為例, 商鋪(shop/mall)、商品(product/commodity)、價格(price)、快遞 (express)、訂單(order)等是其行業用語, 再具體到頁面, title/describe/comment/list/sort等的語義十分明顯, 不同電商網站的用詞基本上是同一套. HTML標簽對于數據的展示也是規范的, 比如標題用<h>和<p>標簽, 列表用多個被大<div>塊包含的<li>標簽描述.
綜上可見, 只要提供合適的語料和技術上的規范,制定匹配的策略, 就可以找到目標, 提取、組合出結構化的數據. 理論上使用機器訓練似乎更加勝任這些預測工作[11], 但是因為主題爬蟲的前提下, 人工加權是可以實現的, 而且會比為了支撐機器訓練而忙于制造訓練數據省力得多.
本文所討論的自動結構化的關鍵點, 在于如何實現較為精準的標簽匹配, 這里可以通過以下兩種方式實現.
3.2.1 列表的匹配

圖4 站點結構舉例
此項用來分辨類別、商品列表的數據是在頁面的哪一部分. 本文分析多個站點的結構, 如圖4是比較典型的一種.依據圖4網頁中列表的特征: 結構一致、覆蓋此頁大部分、多用div/ul/li標簽, 制定了如圖5所示的流程.
同樣的, 先去除不關網頁結構但占據很多篇幅的代碼和文字, 僅僅留下body標簽的內容并生成結構樹,其中還要把標簽文本去除, 來減少文檔體積以提高后期分析效率. 在標識重復相似節點時, 參考了一種基于節點加權的XML檢測算法[12]和加權頻繁子樹相似度的算法[13], 并作出一定改進, 其算法描述如下:
(1) 將HTML文檔用SAX (Simple API for XML)轉化為一棵帶權樹, 其中class、name、type等屬性應該設置較高的權值, 注意應將相同根節點的同一層次節點的權值之和等于1.
(2) 任意兩棵樹進行相似性的粗略匹配, 將屬性值相等的節點計算相似度: 帶權樹Ta、Tb,N代表兩棵樹的節點數,a1–an和b1–bn代表節點權值, 相似度計算公式:計算得到的相似度如果大于預設的α, 認為相似.
(3) 將(2)得到的相似節點對使用樹編輯距離算法, 計算后的距離值小于給定的閾值β, 便最終確認其節點對是相似重復節點.
“判斷標簽名”這一步是為了解決在網頁中發現多片區域出現相似重復節點的情況, 這時應當給ul/li較高優先, 依次類推. 最后確定了列表的位置, 轉化為XPath(XML路徑語言)并存儲, 供后續頁面解析進行快速匹配.
3.2.2 匹配目標字段的標簽
工程項目竣工結算審計是對基本建設項目竣工結算編制,及有關經濟活動的真實性、合法性、效益性進行審計監督和評價的過程,工程審計是工程造價控制的重要環節,是提高建筑項目管理水平的內在動力,是工程建設項目資金真實性、合法性、效益性的重要保障。
3.2.1節介紹的是如何鎖定目標數據的范圍, 還有一個問題就是如何抓取最終的有價值字段. 本文基于主題爬蟲的特點, 提出一種屬性語義匹配的方案, 首先給每個字段建立一個用于預測的詞庫, 然后進行全部/局部匹配, 計算得到權值之后進行比較以實現預測.
假如要匹配商品名稱, 本文設定了詞庫和權值見表1.

表1 詞庫
因為代碼命名常采用縮寫, 當標簽的id屬性局部匹配(本文推薦50%)時即加上該權值, 一些用詞經常是把縮寫也納入詞庫, 并且權值應該更高. 匹配計算過程如下:


因此可以得出結論: 描述商品名稱字段的是標簽1.
還有一點需要注意的是, 標簽描述屬性不只是id也可能是name, 還有的情況是自定義的屬性, 這就需要在原來的算法上加以延伸, 變成決策樹的模型, 這里不再展開討論.
上文提出了自動結構化的一些細節策略和算法,但一些加權規則和語料是需要人工輸入的. 為了實現系統和數據的分離, 提出一種稱為匹配模板的預設數據, 其應當包含如下數據:
(1) 列表結構優先匹配排序, 其包含數據如div/ul/li、div/p、p/p 等.
(2) 節點屬性相似度權值, 其包含數據如name(10),id(15), class(10)等.
(3) 目標字段和匹配詞庫, 其包含字段名和詞庫(用詞和權值).

圖6 預設匹配模板
4.1節所述的利用模板分析出目標字段的匹配路徑, 實際上是主題爬蟲中對于多個源站的差異部分, 而剩下的爬蟲程序運作是高度一致的, 無需重新組織代碼. 遇到一個陌生站點, 只需在爬取前進行模板分析,得到目標字段的位置信息(本文采取XPath), 那么后續開啟的整個爬蟲程序只要沿用他們即可, 在性能上和與原爬蟲的協作設計上都不會有影響.
如果在后續還需要支持分時段和分布式, 那么結構分析結果還應該保存下來, 實現不同時段、不同機器間的復用.
綜上所述, 本文實現了如圖7所示架構的系統.

圖7 系統架構
系統簡述:
(1) 使用者傳入一個入口地址觸發本系統, 抓取工作同期開啟, 它等待主線程的任務.
(2) 較于普通爬蟲系統新增了模板分析部分, 引擎會先進行判斷, 如果是舊網站, 則使用以前分析產生的規則.
(3) 遇到新的站點, 則交由結構分析器, 根據預設的匹配模板, 逐步分析出列表數據、目標字段的位置信息, 將產生的XPath存儲在分析器實例中.
(4) 開啟爬蟲的運作流程, 根據層級提交給分析器解析出所需字段, 完成結構化.

圖8 分析器實例XPath
選取了幾個主流的電商網站進行爬取, 系統能夠正常運行. 圖8為國美網站的分析器實例得到的XPath.為了方便展示, 將圖8的數據用一個腳本進行樹形輸出, 并且僅選取幾個子節點, 得到的結構化數據如圖9所示.

圖9 結構化數據展示
可以看到, 所實現的系統抓取到的數據已經被結構化, 且表現良好.
本文提出的多種匹配方案都包含了加權預測的過程, 會有一定的錯誤率, 這個指標是評價本套方案優良的關鍵.
這里采取的測試手段是: 將抓取到的商品的價格進行正確性判斷, 當其為亂碼、空文本、非數字時, 可以斷定結構化出錯. 表2是多個站點的測試結果.

表2 錯誤率測試
可以看到, 大型站點的錯誤率較高, 而ECShop這類通用商城程序的結構化效果很理想, 原因在其設計更加規范, 命名的可讀性較強. 總的來說, 本文的方案表現良好, 在詞庫和權值參數的調優上還有一定的進步空間.
自動結構化抓取的意義在于, 能夠在更短的時間內獲得更多的數據. 本次實驗進行爬取速度的測試, 將提出的自動結構化爬蟲系統與手工編寫抓取規則的專用爬蟲系統進行對比, 在同一時刻對京東數據進行采集, 并實時統計抓取的商品數量. 運行結果如圖10所示.

圖10 兩種爬蟲系統的爬取速度對比
結果表明, 專用爬蟲自系統啟動就一直穩定抓取,而自動結構化爬蟲會滯后一段時間, 隨后其曲線和專用爬蟲十分相近. 原因是顯而易見的, 自動結構化的系統需要進行模板分析過程, 在分析之后會生成匹配規則, 為后續的頁面數據匹配過程所使用, 此時系統的運行邏輯和專用爬蟲無異. 對于電商整站采集的總時間(可能長達數小時)來說, 一開始數秒的延后時間是可以據略不計的, 而其卻可以輕易遷移到別的站點, 又省下了大量開發時間, 隨著源站需求的增加, 自動結構化系統的優勢會更加明顯.
本文提出了一種針對電商網站可以自動結構化數據的主題爬蟲方案, 由于網站結構嚴謹的層次劃分和網頁代碼的命名規范性, 使得頁面類型、結構分析和標簽匹配的自動化成為可能, 這里使用了相似重復判斷和目標字段標簽匹配的算法作為支撐, 實驗證明提出的方案具有可行性和優良性.
本文亦將方案進行系統實現, 其架構是在普通爬蟲系統基礎上新增了模板分析的部分, 在初次爬取某一站點前, 分析得到所有目標字段的匹配路徑, 剩下的工作跟普通爬蟲系統無異, 即性能和系統邏輯不會受到影響.
除電商網站外, 其他主題爬蟲亦可針對其領域進行層次、結構和字段語料的分析, 實現自動結構化以節省人工編碼成本.