WebMagic指北

WebMagic指北

一、快速開始

WebMagic主要包含兩個jar包:webmagic-core-{version}.jarwebmagic-extension-{version}.jar。在項目中添加這兩個包的依賴全蝶,即可使用WebMagic狂男。

WebMagic默認使用Maven管理依賴古沥,但是你也可以不依賴Maven進行使用秒赤。

開源地址

參考文檔


編寫基本爬蟲

二址晕、實現(xiàn)一個PageProcessor

這部分我們直接通過GithubRepoPageProcessor這個例子來介紹PageProcessor的編寫方式致讥。我將PageProcessor的定制分為三個部分仅仆,分別是爬蟲的配置、頁面元素的抽取和鏈接的發(fā)現(xiàn)拄踪。

public class GithubRepoPageProcessor implements PageProcessor {

    // 部分一:抓取網(wǎng)站的相關(guān)配置蝇恶,包括編碼、抓取間隔惶桐、重試次數(shù)等
    private Site site = Site.me().setRetryTimes(3).setSleepTime(1000);

    @Override
    // process是定制爬蟲邏輯的核心接口撮弧,在這里編寫抽取邏輯
    public void process(Page page) {
        // 部分二:定義如何抽取頁面信息,并保存下來
        page.putField("author", page.getUrl().regex("https://github\\.com/(\\w+)/.*").toString());
        page.putField("name", page.getHtml().xpath("http://h1[@class='entry-title public']/strong/a/text()").toString());
        if (page.getResultItems().get("name") == null) {
            //skip this page
            page.setSkip(true);
        }
        page.putField("readme", page.getHtml().xpath("http://div[@id='readme']/tidyText()"));

        // 部分三:從頁面發(fā)現(xiàn)后續(xù)的url地址來抓取
        page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/[\\w\\-]+/[\\w\\-]+)").all());
    }

    @Override
    public Site getSite() {
        return site;
    }

    public static void main(String[] args) {

        Spider.create(new GithubRepoPageProcessor())
                //從"https://github.com/code4craft"開始抓
                .addUrl("https://github.com/code4craft")
                //開啟5個線程抓取
                .thread(5)
                //啟動爬蟲
                .run();
    }
}
1姚糊、爬蟲的配置

第一部分關(guān)于爬蟲的配置贿衍,包括編碼、抓取間隔救恨、超時時間贸辈、重試次數(shù)等,也包括一些模擬的參數(shù)肠槽,例如User Agent擎淤、cookie奢啥,以及代理的設(shè)置,我們會在第5章-“爬蟲的配置”里進行介紹嘴拢。在這里我們先簡單設(shè)置一下:重試次數(shù)為3次桩盲,抓取間隔為一秒。

2席吴、頁面元素的抽取

第二部分是爬蟲的核心部分:對于下載到的Html頁面赌结,你如何從中抽取到你想要的信息?WebMagic里主要使用了三種抽取技術(shù):XPath孝冒、正則表達式和CSS選擇器柬姚。另外,對于JSON格式的內(nèi)容庄涡,可使用JsonPath進行解析量承。

  1. XPath

    XPath本來是用于XML中獲取元素的一種查詢語言,但是用于Html也是比較方便的啼染。例如:

     page.getHtml().xpath("http://h1[@class='entry-title public']/strong/a/text()")
    

    這段代碼使用了XPath宴合,它的意思是“查找所有class屬性為'entry-title public'的h1元素,并找到他的strong子節(jié)點的a子節(jié)點迹鹅,并提取a節(jié)點的文本信息”卦洽。 對應(yīng)的Html是這樣子的:

104607_Aqq8_190591.png
  1. CSS選擇器

    CSS選擇器是與XPath類似的語言。如果大家做過前端開發(fā)斜棚,肯定知道$('h1.entry-title')這種寫法的含義阀蒂。客觀的說弟蚀,它比XPath寫起來要簡單一些蚤霞,但是如果寫復(fù)雜一點的抽取規(guī)則,就相對要麻煩一點义钉。

  2. 正則表達式

    正則表達式則是一種通用的文本抽取語言昧绣。

     page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
    

    這段代碼就用到了正則表達式,它表示匹配所有"https://github.com/code4craft/webmagic"這樣的鏈接捶闸。

  3. JsonPath

    JsonPath是于XPath很類似的一個語言夜畴,它用于從Json中快速定位一條內(nèi)容。WebMagic中使用的JsonPath格式可以參考這里:https://code.google.com/p/json-path/

3删壮、鏈接的發(fā)現(xiàn)

有了處理頁面的邏輯贪绘,我們的爬蟲就接近完工了!

但是現(xiàn)在還有一個問題:一個站點的頁面是很多的央碟,一開始我們不可能全部列舉出來税灌,于是如何發(fā)現(xiàn)后續(xù)的鏈接,是一個爬蟲不可缺少的一部分。

page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());

這段代碼的分為兩部分菱涤,page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all()用于獲取所有滿足"(https:/ /github.com/\w+/\w+)"這個正則表達式的鏈接苞也,page.addTargetRequests()則將這些鏈接加入到待抓取的隊列中去。

三粘秆、 使用Selectable抽取元素

Selectable相關(guān)的抽取元素鏈式API是WebMagic的一個核心功能墩朦。使用Selectable接口,你可以直接完成頁面元素的鏈式抽取翻擒,也無需去關(guān)心抽取的細節(jié)。

在剛才的例子中可以看到牛哺,page.getHtml()返回的是一個Html對象陋气,它實現(xiàn)了Selectable接口。這個接口包含一些重要的方法引润,我將它分為兩類:抽取部分和獲取結(jié)果部分巩趁。

1、 抽取部分API:
方法 說明 示例
xpath(String xpath) 使用XPath選擇 html.xpath("http://div[@class='title']")
$(String selector) 使用Css選擇器選擇 html.$("div.title")
$(String selector,String attr) 使用Css選擇器選擇 html.$("div.title","text")
css(String selector) 功能同$()淳附,使用Css選擇器選擇 html.css("div.title")
links() 選擇所有鏈接 html.links()
regex(String regex) 使用正則表達式抽取 html.regex("(.*?)")
regex(String regex,int group) 使用正則表達式抽取议慰,并指定捕獲組 html.regex("(.*?)",1)
replace(String regex, String replacement) 替換內(nèi)容 html.replace("","")

這部分抽取API返回的都是一個Selectable接口,意思是說奴曙,抽取是支持鏈式調(diào)用的别凹。下面我用一個實例來講解鏈式API的使用。

例如洽糟,我現(xiàn)在要抓取github上所有的Java項目炉菲,這些項目可以在https://github.com/search?l=Java&p=1&q=stars%3A%3E1&s=stars&type=Repositories搜索結(jié)果中看到。

為了避免抓取范圍太寬坤溃,我指定只從分頁部分抓取鏈接拍霜。這個抓取規(guī)則是比較復(fù)雜的,我會要怎么寫呢薪介?

151454_2T01_190591.png

首先看到頁面的html結(jié)構(gòu)是這個樣子的:

151632_88Oq_190591.png

那么我可以先用CSS選擇器提取出這個div祠饺,然后在取到所有的鏈接。為了保險起見汁政,我再使用正則表達式限定一下提取出的URL的格式道偷,那么最終的寫法是這樣子的:

List<String> urls = page.getHtml().css("div.pagination").links().regex(".*/search/\?l=java.*").all();

然后,我們可以把這些URL加到抓取列表中去:

List<String> urls = page.getHtml().css("div.pagination").links().regex(".*/search/\?l=java.*").all();
page.addTargetRequests(urls);

是不是比較簡單烂完?除了發(fā)現(xiàn)鏈接试疙,Selectable的鏈式抽取還可以完成很多工作。我們會在第9章示例中再講到抠蚣。

2祝旷、獲取結(jié)果的API:

當鏈式調(diào)用結(jié)束時,我們一般都想要拿到一個字符串類型的結(jié)果。這時候就需要用到獲取結(jié)果的API了怀跛。我們知道距贷,一條抽取規(guī)則,無論是XPath吻谋、CSS選擇器或者正則表達式忠蝗,總有可能抽取到多條元素。WebMagic對這些進行了統(tǒng)一漓拾,你可以通過不同的API獲取到一個或者多個元素阁最。

方法 說明 示例
get() 返回一條String類型的結(jié)果 String link= html.links().get()
toString() 功能同get(),返回一條String類型的結(jié)果 String link= html.links().toString()
all() 返回所有抽取結(jié)果 List links= html.links().all()
match() 是否有匹配結(jié)果 if (html.links().match()){ xxx; }

例如骇两,我們知道頁面只會有一條結(jié)果速种,那么可以使用selectable.get()或者selectable.toString()拿到這條結(jié)果。

這里selectable.toString()采用了toString()這個接口低千,是為了在輸出以及和一些框架結(jié)合的時候配阵,更加方便。因為一般情況下示血,我們都只需要選擇一個元素棋傍!

selectable.all()則會獲取到所有元素。

好了难审,到現(xiàn)在為止瘫拣,在回過頭看看3.1中的GithubRepoPageProcessor,可能就覺得更加清晰了吧剔宪?指定main方法拂铡,已經(jīng)可以看到抓取結(jié)果在控制臺輸出了。

四葱绒、使用Pipeline保存結(jié)果

好了感帅,爬蟲編寫完成,現(xiàn)在我們可能還有一個問題:我如果想把抓取的結(jié)果保存下來地淀,要怎么做呢失球?WebMagic用于保存結(jié)果的組件叫做Pipeline。例如我們通過“控制臺輸出結(jié)果”這件事也是通過一個內(nèi)置的Pipeline完成的帮毁,它叫做ConsolePipeline实苞。那么,我現(xiàn)在想要把結(jié)果用Json的格式保存下來烈疚,怎么做呢黔牵?我只需要將Pipeline的實現(xiàn)換成"JsonFilePipeline"就可以了。

public static void main(String[] args) {
    Spider.create(new GithubRepoPageProcessor())
            //從"https://github.com/code4craft"開始抓
            .addUrl("https://github.com/code4craft")
            .addPipeline(new JsonFilePipeline("D:\\webmagic\\"))
            //開啟5個線程抓取
            .thread(5)
            //啟動爬蟲
            .run();
}

這樣子下載下來的文件就會保存在D盤的webmagic目錄中了爷肝。

通過定制Pipeline猾浦,我們還可以實現(xiàn)保存結(jié)果到文件陆错、數(shù)據(jù)庫等一系列功能。這個會在第7章“抽取結(jié)果的處理”中介紹金赦。

至此為止音瓷,我們已經(jīng)完成了一個基本爬蟲的編寫,也具有了一些定制功能夹抗。

五绳慎、爬蟲的配置、啟動和終止

1漠烧、Spider

Spider是爬蟲啟動的入口杏愤。在啟動爬蟲之前,我們需要使用一個PageProcessor創(chuàng)建一個Spider對象已脓,然后使用run()進行啟動声邦。同時Spider的其他組件(Downloader、Scheduler摆舟、Pipeline)都可以通過set方法來進行設(shè)置。

方法 說明 示例
create(PageProcessor) 創(chuàng)建Spider Spider.create(new GithubRepoProcessor())
addUrl(String…) 添加初始的URL spider .addUrl("http://webmagic.io/docs/")
addRequest(Request...) 添加初始的Request spider .addRequest("http://webmagic.io/docs/")
thread(n) 開啟n個線程 spider.thread(5)
run() 啟動邓了,會阻塞當前線程執(zhí)行 spider.run()
start()/runAsync() 異步啟動恨诱,當前線程繼續(xù)執(zhí)行 spider.start()
stop() 停止爬蟲 spider.stop()
test(String) 抓取一個頁面進行測試 spider .test("http://webmagic.io/docs/")
addPipeline(Pipeline) 添加一個Pipeline,一個Spider可以有多個Pipeline spider .addPipeline(new ConsolePipeline())
setScheduler(Scheduler) 設(shè)置Scheduler骗炉,一個Spider只能有個一個Scheduler spider.setScheduler(new RedisScheduler())
setDownloader(Downloader) 設(shè)置Downloader照宝,一個Spider只能有個一個Downloader spider .setDownloader(new SeleniumDownloader())
get(String) 同步調(diào)用,并直接取得結(jié)果 ResultItems result = spider .get("http://webmagic.io/docs/")
getAll(String…) 同步調(diào)用句葵,并直接取得一堆結(jié)果 List<ResultItems> results = spider .getAll("http://webmagic.io/docs/", "http://webmagic.io/xxx")
2厕鹃、Site

對站點本身的一些配置信息,例如編碼乍丈、HTTP頭剂碴、超時時間、重試策略等轻专、代理等忆矛,都可以通過設(shè)置Site對象來進行配置。

方法 說明 示例
setCharset(String) 設(shè)置編碼 site.setCharset("utf-8")
setUserAgent(String) 設(shè)置UserAgent site.setUserAgent("Spider")
setTimeOut(int) 設(shè)置超時時間请垛,單位是毫秒 site.setTimeOut(3000)
setRetryTimes(int) 設(shè)置重試次數(shù) site.setRetryTimes(3)
setCycleRetryTimes(int) 設(shè)置循環(huán)重試次數(shù) site.setCycleRetryTimes(3)
addCookie(String,String) 添加一條cookie site.addCookie("dotcomt_user","code4craft")
setDomain(String) 設(shè)置域名催训,需設(shè)置域名后,addCookie才可生效 site.setDomain("github.com")
addHeader(String,String) 添加一條addHeader site.addHeader("Referer","https://github.com")
setHttpProxy(HttpHost) 設(shè)置Http代理 site.setHttpProxy(new HttpHost("127.0.0.1",8080))

其中循環(huán)重試cycleRetry是0.3.0版本加入的機制宗收。

該機制會將下載失敗的url重新放入隊列尾部重試漫拭,直到達到重試次數(shù),以保證不因為某些網(wǎng)絡(luò)原因漏抓頁面混稽。

配置代理

從0.7.1版本開始采驻,WebMagic開始使用了新的代理APIProxyProvider审胚。因為相對于Site的“配置”,ProxyProvider定位更多是一個“組件”挑宠,所以代理不再從Site設(shè)置菲盾,而是由HttpClientDownloader設(shè)置。

API 說明
HttpClientDownloader.setProxyProvider(ProxyProvider proxyProvider) 設(shè)置代理

ProxyProvider有一個默認實現(xiàn):SimpleProxyProvider各淀。它是一個基于簡單Round-Robin的懒鉴、沒有失敗檢查的ProxyProvider∷榻剑可以配置任意個候選代理临谱,每次會按順序挑選一個代理使用。它適合用在自己搭建的比較穩(wěn)定的代理的場景奴璃。

代理示例:

  1. 設(shè)置單一的普通HTTP代理為101.101.101.101的8888端口悉默,并設(shè)置密碼為"username","password"
    HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
    httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(new Proxy("101.101.101.101",8888,"username","password")));
    spider.setDownloader(httpClientDownloader);
  1. 設(shè)置代理池,其中包括101.101.101.101和102.102.102.102兩個IP苟穆,沒有密碼
    HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
    httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(
    new Proxy("101.101.101.101",8888)
    ,new Proxy("102.102.102.102",8888)));
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抄课,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子雳旅,更是在濱河造成了極大的恐慌跟磨,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攒盈,死亡現(xiàn)場離奇詭異抵拘,居然都是意外死亡,警方通過查閱死者的電腦和手機型豁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門僵蛛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迎变,你說我怎么就攤上這事充尉。” “怎么了衣形?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵喉酌,是天一觀的道長。 經(jīng)常有香客問我泵喘,道長泪电,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上揽趾,老公的妹妹穿的比我還像新娘姚垃。我一直安慰自己,他們只是感情好顿苇,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布瓜富。 她就那樣靜靜地躺著珊肃,像睡著了一般旺隙。 火紅的嫁衣襯著肌膚如雪绒极。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天蔬捷,我揣著相機與錄音垄提,去河邊找鬼。 笑死周拐,一個胖子當著我的面吹牛铡俐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妥粟,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼审丘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了勾给?” 一聲冷哼從身側(cè)響起滩报,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎播急,沒想到半個月后露泊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡旅择,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了侣姆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片生真。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捺宗,靈堂內(nèi)的尸體忽然破棺而出柱蟀,到底是詐尸還是另有隱情,我是刑警寧澤蚜厉,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布长已,位于F島的核電站,受9級特大地震影響昼牛,放射性物質(zhì)發(fā)生泄漏术瓮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一贰健、第九天 我趴在偏房一處隱蔽的房頂上張望胞四。 院中可真熱鬧,春花似錦伶椿、人聲如沸辜伟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽导狡。三九已至约巷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旱捧,已是汗流浹背独郎。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留廊佩,地道東北人囚聚。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像标锄,于是被迫代替她去往敵國和親顽铸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理料皇,服務(wù)發(fā)現(xiàn)谓松,斷路器,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 這個項目也是初窺python爬蟲的一個項目践剂,也是我的畢業(yè)設(shè)計鬼譬,當時選題的時候,發(fā)現(xiàn)大多數(shù)人選擇的都是網(wǎng)站類逊脯,實在是...
    夢航韓語閱讀 2,999評論 2 37
  • 你爬了嗎优质? 要玩大數(shù)據(jù),沒有數(shù)據(jù)怎么玩军洼?這里推薦一些33款開源爬蟲軟件給大家巩螃。 爬蟲,即網(wǎng)絡(luò)爬蟲匕争,是一種自動獲取網(wǎng)...
    Albert新榮閱讀 2,227評論 0 8
  • 一避乏、Gecco是什么 Gecco是一款用java語言開發(fā)的輕量化的易用的網(wǎng)絡(luò)爬蟲,不同于Nutch這樣的面向搜索引...
    4ea0af17fd67閱讀 2,038評論 0 1
  • 經(jīng)常會有一些需要在多臺線上服務(wù)器執(zhí)行同樣的操作甘桑,最近學(xué)習到了幾個命令可以極大的方便這方面的操作拍皮。這幾條命令其實都是...
    allen哦閱讀 4,464評論 0 1