本文是WebMagic文檔的一部分。系列文章寫完後,會整合到WebMagic新版文檔中。css
在WebMagic裏,實現一個基本的爬蟲只須要編寫一個類,實現PageProcessor
接口便可。這個類基本上包含了抓取一個網站,你須要寫的全部代碼。html
以以前的GithubRepoPageProcessor
爲例,我將PageProcessor的定製分爲三個部分,分別是爬蟲的配置、頁面元素的抽取和連接的發現。前端
public class GithubRepoPageProcessor implements PageProcessor { // 部分一:抓取網站的相關配置,包括編碼、抓取間隔、重試次數等 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("//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("//div[@id='readme']/tidyText()")); // 部分三:從頁面發現後續的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(); } }
第一部分關於爬蟲的配置,包括編碼、抓取間隔、超時時間、重試次數等,也包括一些模擬的參數,例如User Agent、cookie,以及代理的設置,咱們會在第5章-「爬蟲的配置」裏進行介紹。在這裏咱們先簡單設置一下:重試次數爲3次,抓取間隔爲一秒。java
第二部分是爬蟲的核心部分:對於下載到的Html頁面,你如何從中抽取到你想要的信息?WebMagic裏主要使用了三種抽取技術:XPath、正則表達式和CSS選擇器。git
XPathgithub
XPath原本是用於XML中獲取元素的一種查詢語言,可是用於Html也是比較方便的。例如:web
page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()")
這段代碼使用了XPath,它的意思是「查找全部class屬性爲'entry-title public'的h1元素,並找到他的strong子節點的a子節點,並提取a節點的文本信息」。 對應的Html是這樣子的:正則表達式
CSS選擇器數據庫
CSS選擇器是與XPath相似的語言。若是你們作過前端開發,確定知道$('h1.entry-title')這種寫法的含義。客觀的說,它比XPath寫起來要簡單一些,可是若是寫複雜一點的抽取規則,就相對要麻煩一點。cookie
正則表達式
正則表達式則是一種通用的文本抽取語言。
page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
這段代碼就用到了正則表達式,它表示匹配全部"https://github.com/code4craft/webmagic"這樣的連接。
XPath、CSS選擇器和正則表達式的具體用法會在第4章「抽取工具詳解」中講到。
有了處理頁面的邏輯,咱們的爬蟲就接近完工了!
可是如今還有一個問題:一個站點的頁面是不少的,一開始咱們不可能所有列舉出來,因而如何發現後續的連接,是一個爬蟲不可缺乏的一部分。
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
相關的鏈式API是WebMagic的一個核心功能。使用Selectable接口,你能夠直接完成頁面元素的鏈式抽取,也無需去關心抽取的細節。
在剛纔的例子中能夠看到,page.getHtml()返回的是一個Html
對象,它實現了Selectable
接口。這個接口包含一些重要的方法,我將它分爲兩類:抽取部分和獲取結果部分。
方法 | 說明 | 示例 |
---|---|---|
xpath(String xpath) | 使用XPath選擇 | html.xpath("//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("<div>(.*?)</div>") |
regex(String regex,int group) | 使用正則表達式抽取,並指定捕獲組 | html.regex("<div>(.*?)</div>",1) |
replace(String regex, String replacement) | 替換內容 | html.replace("<script>.*</script>","") |
這部分抽取API返回的都是一個Selectable
接口,意思是說,抽取是支持鏈式調用的。下面我用一個實例來說解鏈式API的使用。
例如,我如今要抓取github上全部的Java項目,這些項目能夠在https://github.com/search?l=Java&p=1&q=stars%3A%3E1&s=stars&type=Repositories搜索結果中看到。
爲了不抓取範圍太寬,我指定只從分頁部分抓取連接。這個抓取規則是比較複雜的,我會要怎麼寫呢?
首先看到頁面的html結構是這個樣子的:
那麼我能夠先用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);
是否是比較簡單?除了發現連接,Selectable的鏈式抽取還能夠完成不少工做。咱們會在第9章示例中再講到。
當鏈式調用結束時,咱們通常都想要拿到一個字符串類型的結果。這時候就須要用到獲取結果的API了。咱們知道,一條抽取規則,不管是XPath、CSS選擇器或者正則表達式,總有可能抽取到多條元素。WebMagic對這些進行了統一,你能夠經過不一樣的API獲取到一個或者多個元素。
方法 | 說明 | 示例 |
---|---|---|
get() | 返回一條String類型的結果 | String link= html.links().get() |
toString() | 功能同get(),返回一條String類型的結果 | String link= html.links().toString() |
all() | 返回全部抽取結果 | List<String> links= html.links().all() |
match() | 是否有匹配結果 | if (html.links().match()){ xxx; } |
例如,咱們知道頁面只會有一條結果,那麼能夠使用selectable.get()或者selectable.toString()拿到這條結果。
這裏selectable.toString()採用了toString()這個接口,是爲了在輸出以及和一些框架結合的時候,更加方便。由於通常狀況下,咱們都只須要選擇一個元素!
selectable.all()則會獲取到全部元素。
好了,到如今爲止,在回過頭看看3.1中的GithubRepoPageProcessor,可能就以爲更加清晰了吧?指定main方法,已經能夠看到抓取結果在控制檯輸出了。
好了,爬蟲編寫完成,如今咱們可能還有一個問題:我若是想把抓取的結果保存下來,要怎麼作呢?WebMagic用於保存結果的組件叫作Pipeline
。例如咱們經過「控制檯輸出結果」這件事也是經過一個內置的Pipeline完成的,它叫作ConsolePipeline
。那麼,我如今想要把結果用Json的格式保存下來,怎麼作呢?我只須要將Pipeline的實現換成"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,咱們還能夠實現保存結果到文件、數據庫等一系列功能。這個會在第7章「抽取結果的處理」中介紹。
至此爲止,咱們已經完成了一個基本爬蟲的編寫,也具備了一些定製功能。