1. 什麼是網絡爬蟲?css
在大數據時代,信息的採集是一項重要的工做,而互聯網中的數據是海量的,若是單純靠人力進行信息採集,不只低效繁瑣,蒐集的成本也會提升。如何自動高效地獲取互聯網中咱們感興趣的信息併爲咱們所用是一個重要的問題,而爬蟲技術就是爲了解決這些問題而生的。html
網絡爬蟲(Web crawler)也叫作網絡機器人,能夠代替人們自動地在互聯網中進行數據信息的採集與整理。它是一種按照必定的規則,自動地抓取萬維網信息的程序或者腳本,能夠自動採集全部其可以訪問到的頁面內容,以獲取相關數據。java
從功能上來說,爬蟲通常分爲數據採集,處理,儲存三個部分。爬蟲從一個或若干初始網頁的URL開始,得到初始網頁上的URL,在抓取網頁的過程當中,不斷從當前頁面上抽取新的URL放入隊列,直到知足系統的必定中止條件。git
2. 網絡爬蟲的做用github
1.能夠實現搜索引擎web
咱們學會了爬蟲編寫以後,就能夠利用爬蟲自動地採集互聯網中的信息,採集回來後進行相應的存儲或處理,在須要檢索某些信息的時候,只需在採集回來的信息中進行檢索,即實現了私人的搜索引擎。正則表達式
2.大數據時代,可讓咱們獲取更多的數據源redis
在進行大數據分析或者進行數據挖掘的時候,須要有數據源進行分析。咱們能夠從某些提供數據統計的網站得到,也能夠從某些文獻或內部資料中得到,可是這些得到數據的方式,有時很難知足咱們對數據的需求,而手動從互聯網中去尋找這些數據,則耗費的精力過大。此時就能夠利用爬蟲技術,自動地從互聯網中獲取咱們感興趣的數據內容,並將這些數據內容爬取回來,做爲咱們的數據源,再進行更深層次的數據分析,並得到更多有價值的信息。算法
3. 能夠更好地進行搜索引擎優化(SEO)數據庫
對於不少SEO從業者來講,爲了更好的完成工做,那麼就必需要對搜索引擎的工做原理很是清楚,同時也須要掌握搜索引擎爬蟲的工做原理。而學習爬蟲,能夠更深層次地理解搜索引擎爬蟲的工做原理,這樣在進行搜索引擎優化時,才能知己知彼,百戰不殆。
3.網絡爬蟲如何使用?
爬蟲底層兩大核心:
(1).HttpClient:網絡爬蟲就是用程序幫助咱們訪問網絡上的資源,咱們一直以來都是使用HTTP協議訪問互聯網的網頁,網絡爬蟲須要編寫程序,在這裏使用一樣的HTTP協議訪問網頁,這裏咱們使用Java的HTTP協議客戶端 HttpClient這個技術,來實現抓取網頁數據。(在Java程序中經過HttpClient技術進行遠程訪問,抓取網頁數據.)
注:若是每次請求都要建立HttpClient, 會有頻繁建立和銷燬的問題, 可使用HttpClient鏈接池來解決這個問題。
(2). Jsoup:咱們抓取到頁面以後,還須要對頁面進行解析。可使用字符串處理工具解析頁面,也可使用正則表達式,可是這些方法都會帶來很大的開發成本,因此咱們須要使用一款專門解析html頁面的技術。(將獲取到的頁面數據轉化爲Dom對象進行解析)
Jsoup介紹:Jsoup 是一款Java 的HTML、XML解析器,可直接解析某個URL地址、HTML文本、文件解析爲DOM對象, 同時它提供了一套很是省力的API,可經過DOM,CSS以及相似於jQuery的操做方法來取出和操做數
5. WebMagic介紹
WebMagic是一款爬蟲框架,其底層用到了上文所介紹使用的HttpClient和Jsoup,讓咱們可以更方便的開發爬蟲。WebMagic的設計目標是儘可能的模塊化,並體現爬蟲的功能特色。這部分提供很是簡單、靈活的API,在基本不改變開發模式的狀況下,編寫一個爬蟲。
WebMagic項目代碼分爲核心和擴展兩部分。核心部分(webmagic-core)是一個精簡的、模塊化的爬蟲實現,而擴展部分(webmagic-extension)則包括一些便利的、實用性的功能, 例如註解模式編寫爬蟲等,同時內置了一些經常使用的組件,便於爬蟲開發。
1).架構介紹
WebMagic的結構分爲Downloader、PageProcessor、Scheduler、Pipeline四大組件,並由Spider將它們彼此組織起來。這四大組件對應爬蟲生命週期中的下載、處理、管理和持久化等功能.
Spider將這幾個組件組織起來,讓它們能夠互相交互,流程化的執行,能夠認爲Spider是一個大的容器,它也是WebMagic邏輯的核心。
WebMagic整體架構圖以下:
2).WebMagic的四個組件
①.Downloader
Downloader負責從互聯網上下載頁面,以便後續處理。WebMagic默認使用了Apache HttpClient做爲下載工具。
②.PageProcessor
PageProcessor負責解析頁面,抽取有用信息,以及發現新的連接。WebMagic使用Jsoup做爲HTML解析工具,並基於其開發瞭解析XPath的工具Xsoup。
在這四個組件中,PageProcessor對於每一個站點每一個頁面都不同,是須要使用者定製的部分。
③.Scheduler
Scheduler負責管理待抓取的URL,以及一些去重的工做。WebMagic默認提供了JDK的內存隊列來管理URL,並用集合來進行去重。也支持使用Redis進行分佈式管理。
④.Pipeline
Pipeline負責抽取結果的處理,包括計算、持久化到文件、數據庫等。WebMagic默認提供了「輸出到控制檯」和「保存到文件」兩種結果處理方案。
Pipeline定義告終果保存的方式,若是你要保存到指定數據庫,則須要編寫對應的Pipeline。對於一類需求通常只需編寫一個Pipeline。
3).用於數據流轉的對象
①. Request
Request是對URL地址的一層封裝,一個Request對應一個URL地址。它是PageProcessor與Downloader交互的載體,也是PageProcessor控制Downloader惟一方式。除了URL自己外,它還包含一個Key-Value結構的字段extra。你能夠在extra中保存一些特殊的屬性,而後在其餘地方讀取,以完成不一樣的功能。例如附加上一個頁面的一些信息等。
②. Page
Page表明了從Downloader下載到的一個頁面——多是HTML,也多是JSON或者其餘文本格式的內容。Page是WebMagic抽取過程的核心對象,它提供一些方法可供抽取、結果保存等。
③. ResultItems
ResultItems至關於一個Map,它保存PageProcessor處理的結果,供Pipeline使用。它的API與Map很相似,值得注意的是它有一個字段skip,若設置爲true,則不該被Pipeline處理。
6.WebMagic如何使用
1). PageProcessor組件的功能
①.抽取元素Selectable
Selectable相關的抽取元素鏈式API是WebMagic的一個核心功能。使用Selectable接口,能夠直接完成頁面元素的鏈式抽取,也無需去關心抽取的細節。page.getHtml()返回的是一個Html對象,它實現了Selectable接口。這部分抽取API返回的都是一個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("\(.\*?)\") |
PageProcessor裏主要使用了三種抽取技術:XPath、CSS選擇器和正則表達式。對於JSON格式的內容,可以使用JsonPath進行解析.
1. XPath
以上是獲取屬性class=mt的div標籤,裏面的h1標籤的內容
2.CSS選擇器
CSS選擇器是與XPath相似的語言。
div.mt>h1表示class爲mt的div標籤下的直接子元素h1標籤
但是使用:nth-child(n)選擇第幾個元素,以下選擇第一個元素
注意:須要使用 > 就是直接子元素才能夠選擇第幾個元素
3.正則表達式
正則表達式則是一種通用的文本抽取語言。在這裏通常用於獲取url地址。
②.獲取結果
當鏈式調用結束時,咱們通常都想要拿到一個字符串類型的結果。這時候就須要用到獲取結果的API了。
咱們知道,一條抽取規則,不管是XPath、CSS選擇器或者正則表達式,總有可能抽取到多條元素。WebMagic對這些進行了統一,能夠經過不一樣的API獲取到一個或者多個元素。
方法 |
說明 |
示例 |
get() |
返回一條String類型的結果 |
String link= html.links().get() |
toString() |
同get(),返回一條String類型的結果 |
String link= html.links().toString() |
all() |
返回全部抽取結果 |
List links= html.links().all() |
當有多條數據的時候,使用get()和toString()都是獲取第一個url地址。
測試結果:
selectable.toString()在輸出以及和一些框架結合的時候,更加方便。由於通常狀況下,咱們都只採用此方法獲取一個元素!
③. 獲取連接
一個站點的頁面是不少的,一開始咱們不可能所有列舉出來,因而如何發現後續的連接,是一個爬蟲不可缺乏的一部分。
下面的例子就是獲取https://www.jd.com/moreSubject.aspx這個頁面中
全部符合https://www.jd.com/news.\\w+?.*正則表達式的url地址
並將這些連接加入到待抓取的隊列中去。
2). Scheduler組件的使用
在解析頁面的時候,極可能會解析出相同的url地址(例如商品標題和商品圖片超連接,並且url同樣),若是不進行處理,一樣的url會解析處理屢次,浪費資源。因此咱們須要有一個url去重的功能。
Scheduler能夠幫助咱們解決以上問題。Scheduler是WebMagic中進行URL管理的組件。通常來講,Scheduler包括兩個做用:
❶對待抓取的URL隊列進行管理。
❷對已抓取的URL進行去重。
WebMagic內置了幾個經常使用的Scheduler。若是隻是在本地執行規模比較小的爬蟲,那麼基本無需定製Scheduler,可是瞭解一下已經提供的幾個Scheduler仍是有意義的。
類 |
說明 |
備註 |
DuplicateRemovedScheduler |
抽象基類,提供一些模板方法 |
繼承它能夠實現本身的功能 |
QueueScheduler |
使用內存隊列保存待抓取URL |
|
PriorityScheduler |
使用帶有優先級的內存隊列保存待抓取URL |
耗費內存較QueueScheduler更大,可是當設置了request.priority以後,只能使用PriorityScheduler纔可以使優先級生效 |
FileCacheQueueScheduler |
使用文件保存抓取URL,能夠在關閉程序並下次啓動時,從以前抓取到的URL繼續抓取 |
需指定路徑,會創建.urls.txt和.cursor.txt兩個文件 |
RedisScheduler |
使用Redis保存抓取隊列,可進行多臺機器同時合做抓取 |
須要安裝並啓動redis |
去除重複連接部分被單獨抽象成了一個接口:DuplicateRemover,從而能夠爲同一個Scheduler選擇不一樣的去重方式,以適應不一樣的須要,目前提供了兩種去重方式。
類 |
說明 |
HashSetDuplicateRemover |
使用HashSet來進行去重,佔用內存較大 |
BloomFilterDuplicateRemover |
使用BloomFilter來進行去重,佔用內存較小,可是可能漏抓頁面 |
RedisScheduler是使用Redis的set進行去重,其餘的Scheduler默認都使用HashSetDuplicateRemover來進行去重。
若是要使用BloomFilter,必需要加入如下依賴:
修改代碼,添加布隆過濾器
❶ HashSetDuplicateRemover
使用java中的HashSet不能重複的特色去重。優勢是容易理解。使用方便。
缺點:佔用內存大,性能較低。
❷.RedisScheduler的set進行去重。
優勢是速度快(Redis自己速度就很快),並且去重不會佔用爬蟲服務器的資源,能夠處理更大數據量的數據爬取。
缺點:須要準備Redis服務器,增長開發和使用成本。
❸.BloomFilterDuplicateRemover
使用布隆過濾器也能夠實現去重。優勢是佔用的內存要比使用HashSet要小的多,也適合大量數據的去重操做。
缺點:有誤判的可能。沒有重複可能會斷定重複,可是重複數據必定會斷定重複。
*網頁內容去重
上文咱們研究了對下載的url地址進行了去重的解決方案,避免一樣的url下載屢次。其實不僅是url須要去重,咱們對下載的網頁內容也須要去重。在網上咱們能夠找到許多內容類似的文章。可是實際咱們只須要其中一個便可,一樣的內容沒有必要下載屢次,那麼如何進行去重就須要進行處理了
❶.指紋碼對比
最多見的去重方案是生成文檔的指紋門。例如對一篇文章進行MD5加密生成一個字符串,咱們能夠認爲這是文章的指紋碼,再和其餘的文章指紋碼對比,一致則說明文章重複。可是這種方式是徹底一致則是重複的,若是文章只是多了幾個標點符號,那仍舊被認爲是重複的,這種方式並不合理。
❷.BloomFilter
這種方式就是咱們以前對url進行去重的方式,使用在這裏的話,也是對文章進行計算獲得一個數,再進行對比,缺點和方法1是同樣的,若是隻有一點點不同,也會認爲不重複,這種方式不合理。
❸.KMP算法
KMP算法是一種改進的字符串匹配算法。KMP算法的關鍵是利用匹配失敗後的信息,儘可能減小模式串與主串的匹配次數以達到快速匹配的目的。可以找到兩個文章有哪些是同樣的,哪些不同。這種方式可以解決前面兩個方式的「只要一點不同就是不重複」的問題。可是它的時空複雜度過高了,不適合大數據量的重複比對。
❹. Simhash簽名
Google 的 simhash 算法產生的簽名,能夠知足上述要求。這個算法並不深奧,比較容易理解。這種算法也是目前Google搜索引擎所目前所使用的網頁去重算法。
simhash是由 Charikar 在2002年提出來的,爲了便於理解儘可能不使用數學公式,分爲這幾步:
1、分詞,把須要判斷文本分詞造成這個文章的特徵單詞。
2、hash,經過hash算法把每一個詞變成hash值,好比「美國」經過hash算法計算爲 100101,「51區」經過hash算法計算爲 101011。這樣咱們的字符串就變成了一串串數字。
3、加權,經過 2步驟的hash生成結果,須要按照單詞的權重造成加權數字串,「美國」的hash值爲「100101」,經過加權計算爲「4 -4 -4 4 -4 4」
「51區」計算爲 「 5 -5 5 -5 5 5」。
4、合併,把上面各個單詞算出來的序列值累加,變成只有一個序列串。
「美國」的 「4 -4 -4 4 -4 4」,「51區」的 「 5 -5 5 -5 5 5」
把每一位進行累加, 「4+5 -4+-5 -4+5 4+-5 -4+5 4+5」à「9 -9 1 -1 1 9」
5、降維,把算出來的 「9 -9 1 -1 1 9」變成 0 1 串,造成最終的simhash簽名。
咱們把庫裏的文本都轉換爲simhash簽名,並轉換爲long類型存儲,空間大大減小。如今咱們雖然解決了空間,可是如何計算兩個simhash的類似度呢?
咱們經過海明距離(Hamming distance)就能夠計算出兩個simhash到底類似不類似。兩個simhash對應二進制(01串)取值不一樣的數量稱爲這兩個simhash的海明距離。
這個項目不能直接使用,由於jar包的問題,須要導入工程simhash,並進行install。
導入對應的依賴:
測試用例:
測試結果:
3). Pipeline組件的使用
Pipeline組件的做用用於保存結果。咱們如今經過「控制檯輸出結果」這件事也是經過一個內置的Pipeline完成的,它叫作ConsolePipeline
。
那麼,我如今想要把結果用保存到文件中,怎麼作呢?只將Pipeline的實現換成"FilePipeline"就能夠了。
7.網絡爬蟲的配置、啓動和終止
1).Spider
Spider是網絡爬蟲啓動的入口。在啓動爬蟲以前,咱們須要使用一個PageProcessor建立一個Spider對象,而後使用run()進行啓動。
Spider的其餘組件(Downloader、Scheduler)能夠經過set方法來進行設置,
Pipeline組件經過add方法進行設置。
方法 |
說明 |
示例 |
create(PageProcessor) |
建立Spider |
Spider.create(new GithubRepoProcessor()) |
addUrl(String…) |
添加初始的URL |
spider .addUrl("http://webmagic.io/docs/") |
thread(n) |
開啓n個線程 |
spider.thread(5) |
run() |
啓動,會阻塞當前線程執行 |
spider.run() |
start()/runAsync() |
異步啓動,當前線程繼續執行 |
spider.start() |
stop() |
中止爬蟲 |
spider.stop() |
addPipeline(Pipeline) |
添加一個Pipeline,一個Spider能夠有多個Pipeline |
spider .addPipeline(new ConsolePipeline()) |
setScheduler(Scheduler) |
設置Scheduler,一個Spider只能有個一個Scheduler |
spider.setScheduler(new RedisScheduler()) |
setDownloader(Downloader) |
設置Downloader,一個Spider只能有個一個Downloader |
spider .setDownloader( new SeleniumDownloader()) |
get(String) |
同步調用,並直接取得結果 |
ResultItems result = spider .get("http://webmagic.io/docs/") |
getAll(String…) |
同步調用,並直接取得一堆結果 |
List<ResultItems> results = spider .getAll("http://webmagic.io/docs/", "http://webmagic.io/xxx") |
2). 爬蟲配置Site
Site.me()能夠對爬蟲進行一些配置配置,包括編碼、抓取間隔、超時時間、重試次數等。在這裏咱們先簡單設置一下:重試次數爲3次,抓取間隔爲一秒。
站點自己的一些配置信息,例如編碼、HTTP頭、超時時間、重試策略等、代理等,均可以經過設置Site對象來進行配置。
方法 |
說明 |
示例 |
setCharset(String) |
設置編碼 |
site.setCharset("utf-8") |
setUserAgent(String) |
設置UserAgent |
site.setUserAgent("Spider") |
setTimeOut(int) |
設置超時時間, 單位是毫秒 |
site.setTimeOut(3000) |
setRetryTimes(int) |
設置重試次數 |
site.setRetryTimes(3) |
setCycleRetryTimes(int) |
設置循環重試次數 |
site.setCycleRetryTimes(3) |
addCookie(String,String) |
添加一條cookie |
site.addCookie("dotcomt_user","code4craft") |
setDomain(String) |
設置域名,需設置域名後,addCookie纔可生效 |
site.setDomain("github.com") |
addHeader(String,String) |
添加一條addHeader |
site.addHeader("Referer","https://github.com") |
setHttpProxy(HttpHost) |
設置Http代理 |
site.setHttpProxy(new HttpHost("127.0.0.1",8080)) |
8.代理服務器
有些網站不容許網絡爬蟲進行數據爬取,由於會加大服務器的壓力。解決此障礙其中一種最有效的方式是經過ip+訪問時間進行鑑別,由於正常人不可能短期開啓太多的頁面,發起太多的請求。
咱們使用的WebMagic能夠很方便的設置爬取數據的時間,可是這樣會大大下降咱們爬取數據的效率,若是不當心ip被禁了,會讓咱們沒法爬去數據,那麼咱們就有必要使用代理服務器來爬取數據。
網絡代理(internet Proxy)是一種特殊的網絡服務,容許一個網絡終端(通常爲客戶端)經過這個服務與另外一個網絡終端(通常爲服務器)進行非直接的鏈接。提供代理服務的電腦系統或其它類型的網絡終端稱爲代理服務器(英文:Proxy Server)。一個完整的代理請求過程爲:客戶端首先與代理服務器建立鏈接,接着根據代理服務器所使用的代理協議,請求對目標服務器建立鏈接、或者得到目標服務器的指定資源。
注: 網上有不少代理服務器的提供商,可是大可能是免費的很差用,付費的效果還不錯。
2).代理服務器的使用
WebMagic使用的代理APIProxyProvider。由於相對於Site的「配置」,ProxyProvider定位更可能是一個「組件」,因此代理再也不從Site設置,而是由HttpClientDownloader設置。
API |
說明 |
HttpClientDownloader.setProxyProvider(ProxyProvider proxyProvider) |
設置代理 |
ProxyProvider有一個默認實現:SimpleProxyProvider。它是一個基於簡單Round-Robin的、沒有失敗檢查的ProxyProvider。能夠配置任意個候選代理,每次會按順序挑選一個代理使用。它適合用在本身搭建的比較穩定的代理的場景。
若是須要根據實際使用狀況對代理服務器進行管理(例如校驗是否可用,按期清理、添加代理服務器等),只須要本身實現APIProxyProvider便可。
使用代理服務器以前,經過查詢能夠獲取ip信息的網站獲得以下結果:
未使用代理服務器以前,利用爬蟲程序抓取此頁面得到的結果爲本地ip地址:
使用代理服務器:
使用代理服務器以後,用爬蟲程序獲取的結果爲代理服務器ip地址:
由此看出使用代理服務成功,而且訪問的ip改成代理服務器的ip.