楊 君,陳春玲,余 瀚
(南京郵電大學 計算機學院,江蘇 南京 210003)
互聯網在人們的生活中無處不在,通過互聯網,人們可以便捷、高效地獲取所需要的各種各樣的信息[1]。然而在信息大爆炸式發展的今天,互聯網上的網絡數據量以其驚人的速度呈幾何級增長。就網頁數據而言,每天都如雨后春筍般涌出。互聯網上的各種信息在帶來便利的同時,因其數據繁雜且數據量眾多,也帶來了信息過載的問題。因此,面對互聯網上的海量數據,如何快速且高效地獲取所需要的數據是一個迫切需要解決的問題。
基于Scrapy框架設計的數據采集系統,可以高效、準確地獲取所需要的數據資源。根據用戶指定的主網及數據類型關鍵字,爬取所需要的數據,并且可以將獲取到的數據進行清洗和分類。高效地數據獲取、數據的實時性、數據的準確性對用戶來說都具有十分重要的實際意義。
Scrapy是用Python編寫實現的網站數據異步爬蟲應用框架。用戶可以根據需求進行修改,使用起來簡單輕巧因而用途十分廣泛,可以應用在包括數據挖掘、信息處理或存儲歷史數據等一系列領域。Scrapy內部實現了包括并發請求、免登錄、URL去重等多種復雜的操作。Scrapy設計理念先進而簡單,效率高,可擴展性好,可移植性高,在主流的操作系統平臺上都具有良好的性能[2]。
Scrapy主要由5個部分組成:Scrapy Engine(Scrapy引擎)、Scheduler(調度器)、Spiders(蜘蛛)、Item Pipeline(數據處理流水線)和Downloader(下載器)[3]。爬取過程是調度器把初始URL交給下載器;下載器向網絡服務器發送服務請求,得到響應后將下載的數據轉交給蜘蛛;蜘蛛-般是用戶自己定義的程序,蜘蛛會對網頁進行詳細的分析,分析得到的待爬取鏈接會請求調度器;調度器再次調度處理這些URL,分析得到的數據,會轉交給數據處理流水線繼續處理[4]。Item會定義數據輸出格式等,最后由Pipeline輸出到文件中,或者存到數據庫中等。總之數據處理流水線對數據進行的后期處理包括過濾、去重和存儲等[5]。
Django框架是基于Python語言編寫的一個開源免費的Web應用框架。Django具備較為完美的模版機制、對象關系映射機制,還能夠創建出動態管理后臺信息的界面。相較于其他基于Python的框架,Django框架更加注重組件之間的可重復利用性和可插拔性,可以相對簡單地開發出功能復雜的帶有數據庫驅動的網站,以及遵守敏捷開發原則和DRY法則[6]。
Django框架是一個松耦合、高內聚的框架,每個由Django驅動的Web應用都有著明確的目的,并且可獨立更改而不影響到其他部分,可以讓開發人員更專注于編寫清晰、易維護的代碼和應用程序的開發。它可以運行在啟用了mod_python或mod_wsgi的Apache2上,或者任何兼容WSGI的Web服務器。一個Django工程中,主要分為3個Python的文件(urls.py、views.py、models.py)和html模板文件(index.html)。urls.py指出了URL調用views.py中所調用的視圖;views.py用來定義程序的業務邏輯,主要實現URL的分發處理和定義處理Web請求的函數;models.py定義了程序的數據存儲結構。index.html是html模板,它描述了這個html頁面是如何設計的。
Django的基本結構是MTV(model,templates,view),其中模型模塊(model)負責與數據有關的全部事務處理,包含數據存取、數據驗證、數據行為以及提供訪問數據庫的API[7]。視圖模塊(view)是業務邏輯處理模塊,負責模型的存取和正確調用模板,用來定義如何渲染Templates,是聯系模型和模板的紐帶。模板模塊(templates)用來定義數據的顯示格式,負責與展現相關的事務[8]。Django的MTV模式實際上只實現了普通MVC模式中的M和V。Django的控制器是框架本身,框架將根據Django的URL配置向合適的視圖函數發送請求。
系統采用典型的B/S(browser/server)架構(見圖1),分為三層:前端、后端、數據庫。

圖1 系統分層架構
前端采用典型Html、Css、JavaScript技術來呈現網頁,Css使用bootstrap樣式庫,JavaScript使用jQuery,并且使用Ajax異步刷新技術。
該系統的架構設計完全基于Django的MTV架構實現。Django URLconf實現了Http請求的URL匹配功能,尋找對應的視圖函數。Django Setting配置了整個系統的設置。
Models與數據庫Mysql實現ORM關系映射,不需要關注數據庫中數據的結構層次,直接通過Models對象來操作數據庫。
按照用戶使用的方便性和對系統需求的分析,基于Scrapy的數據采集系統包含服務器端和瀏覽器端功能模塊,如圖2所示。
2.2.1 服務器端功能模塊
(1)網頁解析模塊:當抓取網頁數據時,需要從訪問到的HTML源碼中提取數據。Scrapy提取數據有自己的一套機制,它們被稱作選擇器(selectors),因為它們通過特定的XPath或正則表達式來“選擇”HTML文件中的某個部分。而這里每個字段的XPath表達式和Python正則表達式都是在任務新建模塊進行配置的。
(2)數據處理模塊:當一個任務完成后,該模塊會將得到的數據進行簡單清洗,比如刪除重復的數據,并且將數據關聯到它們所在的任務種類,方便后面的數據查詢。

圖2 系統功能結構
2.2.2 瀏覽器端功能模塊
(1)系統登錄模塊:判斷客戶端用戶的合法性,如果輸入的用戶驗證信息正確,就可以進入自己的任務管理模塊。新用戶可以點擊“用戶注冊”進入注冊界面,完成自己信息錄入。
(2)任務新建模塊:在該模塊可以新建自己的子任務,并且歸類到自己的任務樹中。需要配置的信息包括任務名稱、任務的主網地址以及該任務所屬父節點,然后新建需要爬取的字段,包括字段名稱、XPath表達式和正則表達式。
(3)任務管理模塊:在該模塊,所有的任務和任務類名會形成一棵任務樹。根節點是用戶名,用戶可以根據自己的需要,手動新建或刪除葉子節點,以增加、刪除任務類別。當刪除父節點時,所有的子節點都會被刪除,其中的任務類別和子任務都會被刪除。還可以通過手動拖動節點,改變任務間關系。
(4)數據查詢模塊:用戶可以通過輸入任務名以及數據關鍵字進行組合搜索,搜索到的數據可以根據需要設置成一頁展示的數據條目數,如一頁五條、一頁二十條或一頁一百條等等。
在當前應用環境中,運用合理的方法設計并抽象出最佳的數據模型,并且在此基礎上搭建上層應用系統,在此基礎上創建的數據庫能有效地存取數據,滿足系統需求和用戶需求。根據該數據采集系統中的設計,采用MySql數據庫。
用戶信息表中包括用戶名、用戶郵箱、用戶號碼、用戶創建時間和更新時間,如表1所示。

表1 用戶信息表結構
任務信息表包括任務英文名、任務中文名、任務目標網址、網址關鍵字、任務創建時間和任務更新時間(見表2),其中網址關鍵字用于任務進行時url的爬取篩選,這樣爬取的url具有針對性,可以過濾大量冗余的url。
字段表結構包括字段英文名、字段中文名、XPath表達式、正則表達式、創建時間和更新時間(見表3),其中XPath表達式用來定位網頁數據位置,正則表達式是根據需要解析數據的。

表3 字段表結構
任務樹表包括節點名稱、父節點名稱、創建時間和更新時間(見表4),其中根節點就是用戶名,剩余非葉子節點的父節點是用戶的任務類型,葉子節點是用戶的單個采集任務,名稱即任務名。

表4 任務樹表結構
Django采用MTV開發模式,在該數據采集系統中,一個完整的請求處理過程如圖3所示。

圖3 數據交互處理過程
(1)用戶在瀏覽器端填寫好表單,前臺使用jQuery進行表單驗證,驗證通過后將表單數據做成Json格式;
(2)jQuery啟動Ajax引擎,使用XMLHttpRequest發送Http請求;
(3)請求通過Django框架的urls.py中定義的路由表,匹配對應URL,請求到達URL對應的視圖;
(4)調用視圖中的相關請求處理函數,對獲取到的請求數據進行處理;
(5)視圖中的方法可以通過ORM訪問底層數據庫,進行操作;
(6)再次通過XMLHttpRequest對象獲取服務器數據;
(7)模板獲取數據,瀏覽器端使用JavaScript操作,將數據渲染在頁面上。
Django的URL映射非常靈活,URLconf中保存了URL模式與所調用視圖函數之間的映射表,系統擁有六大app應用,分別對應六個功能模塊,每個app應用都有自己的URL映射表,包含的URL幾個到幾十個不等,通過正則表達式進行匹配。每個URL都含有自己的視圖函數,視圖函數中包含如常見的POST、GET的HTTP請求,請求數據及請求類型在HTTP的報文中攜帶。Django的視圖負責處理用戶請求并且返回響應,一個視圖函數就是接受了Web請求并且返回Web響應的Python函數,通過使用Python強大的類庫,用戶可以在視圖函數中處理各種復雜的邏輯[9]。Django通過使用對象關系映射(ORM)將模型同關系數據庫連接起來,并提供給用戶易于使用的數據庫API[10]。主要的數據庫ORM包括數據庫表結構的創建和數據的增刪改查,其中字段類型包括CharField、EmailField、URLField、GenericIPAddressField等,字段參數包括null、default、primary_key、db_column、db_index、unique等。提供了Ajax請求方式,不需要刷新整個頁面,僅僅更新需要的數據,降低了服務器的處理時間,帶來了更好的用戶體驗。
以新聞出版廣電總局(http://www.sapprft.gov.cn/)網站為例,用戶想要獲取所有新聞出版物的信息,包括音像、圖書、電子期刊等。首先定義新聞出版物的7個信息字段(名稱,前綴,地區,主辦單位,主管單位,類型,網址)。Scrapy根據定義的字段表,生成用來裝載數據的7個items容器,該容器會在spider中用到,用來承載其抓取下來的實際數據。
Scrapy的整個數據處理流程由Scrapy引擎進行控制,引擎打開第一個域名,在這里就是任務信息表中的start_ulr,即http://www.sapprft.gov.cn/,然后創建一個http.Request請求,并把請求放入任務隊列中,并且構建parse_request方法來處理請求[11]。這里的parse_request是一個回調函數。
請求執行之后,http.Response作為執行結果被返回。請求結果就是response.body,即網頁請求響應內容。然后根據任務信息表中定義的url_xpath來解析response.body獲取需要的urls,可能會獲取0個到幾百個,然后通過allowed_url約束條件,將不符合正則表達式的urls剔除,然后將合理的urls放入任務隊列中,給引擎執行下一步抓取[12]。
同時使用Scrapy Selectors,即字段表中的XPath表達式和正則表達式來解析response.body獲取相應字段的數據轉載進入items容器[13]。XPath在配置時盡量使用html的id、class或者屬性值去定位,這樣既準確又方便,正則表達式是為了去除冗余的數據,匹配需要的數據。
使用yield將items內容傳遞到ItemPipeline[14],引擎將在該處判斷傳遞來的數據是否完整,內容是否重復,最后根據定義的字段表按設定輸出,存儲到相應的數據庫中。
網頁訪問的速度直接影響了抓取的速度,有些網站本身結構清晰,訪問快,自然數據抓取的速度也會非常快[15]。選取的新聞出版廣電總局,網站結構統一,響應速度快,所以在系統開啟任務后,用了短短6~10 s的時間就將幾十萬條出版物信息抓取下來,并且放入數據庫中。當第二次啟動該任務時,時間會縮短,因為系統不需要再去創建數據庫和數據表,也減少了一些任務話費的時間。有些網站訪問速度較慢,如土地交易網,系統花費了28分鐘抓取了200萬條數據,這樣的效率確實不盡如人意,這個問題將在后面的開發中進一步改善。
通過對Scrapy爬蟲框架和Django Web開發框架技術的研究,設計了一種基于Scrapy技術的數據采集系統,并對瀏覽器端和服務器端的功能進行了編碼實現。在系統的設計和實現過程中,對系統的整體設計框架、系統功能設計和數據庫設計進行了詳細說明,采用了模塊化的設計方法,使系統具有良好的可擴展性。通過Django框架中的MTV開發模式實現了瀏覽器端和服務器端的數據交互,利用Scrapy技術實現了網絡爬蟲,jQuery樹插件zTree技術實現了任務管理。通過對該系統的測試,用戶可以根據需求爬取數據,并將數據保存下來,進行數據處理和數據查詢,達到了系統開發的要求。