數據接入框架,純Golang(1.5+)編寫:goDataAccess
數據接入框架,純Golang(1.5+)編寫。
簡介
你肯定碰到過數據抓取的相關問題,比如臃腫的規則、與業務無關的多進程多線程邏輯、訪問被服務器限制、模擬登陸、瀏覽器識別等等。goDataAccess旨在緩解數據抓取的痛點,讓開發者更加舒適的抓取到想要的內容。
數據接入聽起來并不是那么接地氣,簡單的說是抓取、抽取、入庫等一系列操作的集合。最終將我們想要的內容存儲下來。為啥要數據接入?出于各種各樣的 原因,總是需要一些網絡上的資源。我們希望把網絡上的資源接入進現有的服務中來,以提供更好的服務。如果你所從事的是科研工作,則更加避免不了數據的抓取 了,因為你所提出的模型總是需要進行實驗驗證的。所以,goDataAccess值得你花3-5分鐘的時間嘗試一下:)
架構
goDataAccess主要由三部分組成,spider、agent、da。
spider的主要功能是抓取,這里實現了一種類似Scrapy的抓取框架。agent是代理模塊,基于goDataAccess/spider開 發,通過cli.go完成代理的更新、驗證和代理服務(RPC)的啟動。da基于goDataAccess/spider和goDataAccess /agent,可以使用提供的API完成特定業務的數據接入工作。
Spider
spider是一個抓取框架,類似Python下的Scrapy,架構圖如下所示。如果你使用過Scrapy,則很容易的使用goDataAccess/spider。
https://github.com/zhangxiaoyang/goDataAccess/tree/master/spider/example里有示例,可以幫助你快速的編寫一個爬蟲,下面的代碼片段均來自于此。
spider中最重要的是engine,因為啟動一個爬蟲就意味著啟動一個engine,比如這樣子:
engine.NewEngine("crawl_baidu_and_print_it").SetStartUrl(url).Start() 通過SetXXX方法可以設置自定義的模塊,比如SetDownloader、SetProcesser、SetPipeline、SetStartUrls、SetStartUrl、SetConfig(目前提供的配置參數有這些)等。
pipeline
你可能會有疑惑,engine會把結果輸出到哪里呢?所以,你可以這樣(結果會被輸出到控制臺):
engine.NewEngine("crawl_baidu_and_print_it").SetStartUrl(url).AddPipeline(pipeline.NewConsolePipeline()).Start() 目前,pipeline提供FilePipeline和ConsolePipeline兩種輸出,你還可以編寫自己的pipeline,最后通過 AddPipeline方法告訴engine即可。還有一個SetPipeline方法在某些情況下會派上用場,該方法會刪除之前AddPipeline 添加的所有pipeline,并設置為此次調用所指定的pipeline。
processer
processer完成頁面的解析功能,engine默認會使用LazyProcesser,即返回整個頁面。你也可以編寫自己的 Processer完成特定內容的抽取。最終抽取的內容需要通過AddItem方法告訴engine。(為了簡化Processer的編寫,后面會有 QuickEngine和extractor哦,不要走開)
downloader / scheduler
downloader通常情況下不需要重寫,主要負責頁面的下載。scheduler是一個內存隊列,通常也不需要修改。可以通過SetDownloader、SetScheduler來設置自定義的模塊。
extractor
為了簡化Processer的編寫,spider內置了一個基于正則表達式的抽取模塊extractor。extractor有兩個重要的方法,SetScopeRule和SetRules。對于百度百科詞條詳情頁的抽取,使用如下:
items := extractor.NewExtractor().
SetScopeRule(`(?s)<dt class="basicInfo-item name">.*?</dd>`).
SetRules(map[string]string{
"key": `(?s)name">(.*?)</dt>`,
"value": `(?s)value">(.*?)</dd>`,
}).
Extract(resp.Body) SetScopeRule會將頁面分割成若干個相同的Scope,每一個Scope都符合規則(?s)<dt class="basicInfo-item name">.*?</dd>,然后再對每一個Scope執行SetRules的操作,這里會提取2個詞,第一個詞的規則是(?s)name">(.*?)</dt>,第二個詞的規則是(?s)value">(.*?)</dd>, 最終將結果轉換為items。因為每一個item都是key-value的結構,所以抽取的每一個詞都需要一個屬性名稱,這里分別叫做“key”、 “value”。extractor還提供其他的方法,比如TrimHtmlTags,該方法會剔除抽取內容中的html標簽,TrimBlank會剔除 首尾的所有空白字符。
quick_engine
可擴展在一定程度上就以為著繁瑣,鑒于此,quick_engine對現有的模塊進行了封裝。通過加載JSON配置文件可以直接啟動spider,比如這樣:
engine.NewQuickEngine("crawl_baidubaike_with_quickengine.json").Start() 一個典型的配置文件如下:
{
"task_name": "crawl_baidubaike_with_quickengine",
"start_urls": [
"http://baike.baidu.com/view/3179.htm",
"http://baike.baidu.com/subview/2632/19244814.htm"
],
"rules": [
{
"url_match": "http://baike.baidu.com/view",
"base_url": "http://baike.baidu.com",
"item_rule": {
"scope_rule": "(?s)<dt class=\"basicInfo-item name\">.*?</dd>",
"kv_rule": {
"key": "(?s)name\">(.*?)</dt>",
"value": "(?s)value\">(.*?)</dd>"
},
"trim_func": "trim_html_tags"
},
"request_rule": {
"scope_rule": "(?s)<ul class=\"slider maqueeCanvas\">.*?</ul>",
"kv_rule": {
"url1": "href=\"(.*?)\""
},
"trim_func": "trim_html_tags"
},
"merge": true
},
{
"url_match": "http://baike.baidu.com/subview",
"base_url": "http://baike.baidu.com",
"item_rule": {
"scope_rule": "(?s).*",
"kv_rule": {
"name": "(?s)<dd class=\"lemmaWgt-lemmaTitle-title\">.*?<h1>(.*?)</h1>.*?</dd>"
},
"trim_func": "trim_html_tags"
},
"request_rule": {
"scope_rule": "(?s)<ul class=\"slider maqueeCanvas\">.*?</ul>",
"kv_rule": {
"url1": "href=\"(.*?)\""
},
"trim_func": "trim_html_tags"
},
"merge": true
}
],
"output_file": "crawl_baidubaike_with_quickengine_output.txt",
"config": {
"concurrency": 20,
"succ": "baidu"
}
} 需要說明的是,request_rule表示從當前頁面抽取url并放入到scheduler中,當抽取的url為相對路徑時,會自動使用 base_url為前綴。scope_rule對應于extractor的SetScopeRule,kv_rule對應于SetRules。對于 request_rule的kv_rule,規則的key可以隨便指定,此處指定的是url1。merge=true表示將得到的多個item一起輸出 (list形式),merge=false則表示每一個item都單獨輸出(dict形式)。配置文件中的rules為一個列表,可以指定多個規則,規則 是否觸發根據url_match來判斷,url_match是一個正則表達式。多個規則根據url_match自上而下匹配,匹配成功則不再繼續向下匹 配。
Agent
可以通過agent/cli.go使用agent。
go run cli.go update # fetching free proxies go run cli.go validate 'http://m.baidu.com' 'baidu' # picking available proxies which can be used to visit 'http://m.baidu.com'. If response bodies from proxies do not contain 'baidu', proxies will not be picked. go run cli.go serve # RPC service at 127.0.0.1:1234
update、validate命令無需完全執行完,根據需求即可。agent內置了代理抓取規則,默認update命令會讀取該規則。可以通過添 加規則文件到agent/rule,格式需為update.xxx.json。validate則基于rule/validate.json執行。 update和validate命令基于quick_engine編寫。
最后通過serve命令啟動RPC服務,地址為127.0.0.1:1234。可以使用spider內置的plugin,即proxy_plugin.go,可以通過其與agent服務連接,參考百度百科的抓取。
DA
da為數據接入模塊,可以使用spider和agent提供的API完成特定業務的數據接入工作。da中會不斷加入基于spider和agent編寫的數據接入代碼,目前有baike.baidu.com和bgp.he.net的示例代碼。
抓取bgp.he.net需要模擬瀏覽器,即包括Cookie和Javascript等認證操作。為此,spider內置了cookie_plugin.go,使用見https://github.com/zhangxiaoyang/goDataAccess/tree/master/da/bgp.he.net/bin/spider.go中。