天天看點

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

突發奇想,覺得有時儲存網頁上的資源非常麻煩,有沒有辦法輸入一個網址就批量抓取對應資源的辦法呢。

需要思考的問題:

1.如何得到網頁url的html源碼呢?

2.如何在浩瀚如海的html中比對出需要的資源位址呢?

3.如何按照得到的資源位址集合批量下載下傳資源呢?

4.下載下傳的資源一般為檔案流,如何生成指定的資源類型并儲存呢?

需要掌握的知識:

1.網絡爬蟲的基礎知識,發送Http請求的方法

2.C# 正規表達式運用,主要是識别html中需要的rul網址

3.UnityWebRequest類檔案流下載下傳

4.C# File類和Stream類等基礎檔案操作

下面分項來進行實作:

關于爬蟲這裡就不進行介紹了,網上其他的地方有很多資料,簡而言之就是采集網頁資訊和資料的程式。

第一步,就是要發送一個Web請求,也可以說是Http請求。

這跟你打開浏覽器輸入一個url位址然後回車産生的效果基本是類似的,網頁上之是以能顯示出正确的資訊和資料,是因為每一個網頁有對應的html源碼,像很多浏覽器例如谷歌浏覽器都是支援檢視網頁源碼的功能,例如下面是我經常去的喵窩的首頁的html的<head>部分:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

html源碼中可以檢視到網頁目前的很多隐藏資訊和資料,其中還有大量的資源連結和樣式表等。值得注意的是,html源碼隻有在網頁全部加載完成之後很可以顯示和檢視,這意味着一個url位址的Web請求響應成功;有成功的情況當然就會有各種各樣失敗的情況,例如我們經常輸入一個rul位址後出現404的提示,這種就是一個Http請求出現錯誤的情況,404表示伺服器未找到請求的網頁。其他的錯誤類型還有很多。為什麼要了解這一點呢,因為之後在發送Http請求時要想辦法對錯誤進行處理或跳過執行下一任務。

我們可以有很多方式來發送Http請求,Unity也更新了Web請求的方式:(以後代碼我就直接截圖了,這個插入代碼功能都不能自動排整齊真的難受)

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

主要用到的類就是UnityWebRequest,和Unity中以前的類WWW有些類似,主要用于檔案的下載下傳與上傳。

要引入以下命名空間:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

UnityAction作為參數主要是用于請求結束後可以自動傳回一個html源碼。它本質上就是個泛型委托:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

泛型的參數可以從沒有到多個,是一個非常好用的類(尤其是在協程的回調中,可以很友善的延時參數傳遞)

當然了,除了Unity内置的發送Web請求的方法,C#也封裝了好幾個類,你可以随便挑一個使用,例如

HttpWebRequest,WebClient,HttpClient等:

比如這樣:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

如果成功通過Web請求得到了指定url位址的html源碼,那就可以執行下一步了。

第二步,收集html中所需要的資料資訊,本例中就是要從這些源碼中找出圖檔的連結位址。

例如可能會有下面這幾種情況:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存
實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存
實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存
實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

總結一下,首先利用html的常用标簽<img>來找可以找到大部分的圖檔,但還是有部分圖檔并不在這些标簽之内。而且有時候,即使是在<img>标簽之内的圖檔位址,還是有可能出現内鍊或是外鍊的差別,外鍊的話直接作為合法的url位址執行即可,但如果是内鍊的話就還要補全域名位址,是以我們還需要想辦法識别一個url的正确域名。

關于如何識别比對以上所說的字元串内容,目前最有效的方法就是正規表達式,下面就列舉在本例中需要使用到的正規表達式:

1.比對url域名位址:

private const string URLRealmCheck = @"(http|https)://(www.)?(\w+(\.)?)+";

2.比對url位址:

private const string URLStringCheck = @"((http|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?";

3.比對html中<img>标簽内的url位址:(不區分大小寫,其中分組<imgUrl>中為所需的url位址)

private const string imgLableCheck = @"<img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r\n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>";

4.比對html中<a>标簽内href屬性的url位址:(不區分大小寫,主要用于深度檢索,其中分組<url>中為所需的url位址)

private const string hrefLinkCheck = @"(?i)<a\s[^>]*?href=(['""]?)(?!javascript|__doPostBack)(?<url>[^'""\s*#<>]+)[^>]*>";

5.指定圖檔類型的比對:(主要用于外鍊)

private const string jpg = @"\.jpg";

private const string png = @"\.png";

關于正規表達式的具體比對用法,網上也有很多教程,這裡就不說了。

給定一個html源碼,下面從兩個方向對圖檔進行比對,先比對外鍊,這裡指定了比對的檔案類型:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

下面是内鍊的比對,先要比對出域名位址:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

有了域名位址之後就可以輕松比對内鍊位址了:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

使用正規表達式需要引入以下命名空間:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

利用正規表達式比對出所有的imgLinks後就可以對其中的圖檔進行依次下載下傳了。

第三步,對有效的圖檔url進行下載下傳傳輸:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

你也可以對這些url進行同步下載下傳傳輸,但這樣可能需要增加額外的最大線程數,而且比較難控制整體的下載下傳進度。

具體的傳輸協程如下:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

值得注意的是,并非隻有成功下載下傳時才調用Complete方法,即使發生了錯誤,也需要調用,這樣避免了一發生錯誤,自動下載下傳就自行終止的情況。正常情況下是即使發生了錯誤,也要跳過執行下一檔案的下載下傳任務。

最後一步就是将下載下傳的資料檔案流轉化為指定類型的檔案并儲存,這裡方法有很多,下面提供一種:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

擴充:

有時單個html中的所有圖檔連結不能完全滿足我們的需求,因為html中的子連結中可能也會有需要的url資源位址,這時我們可以考慮增加更深層次的周遊。那就需要先比對出html中的link位址,然後再得到該link位址的子html源碼,如此進行關于深度比對的循環。

比對html中的子連結可以通過查找<a>标簽的屬性href,上面已經給出過該屬性的正則比對表達式,這裡隻深度比對了一層以供參考:

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存

測試:這裡用深度比對抓取喵窩首頁為jpg格式的圖檔連結并下載下傳,存到D盤中。(UI就随便做的不用在意)

實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存
實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存
實驗:用Unity抓取指定url網頁中的所有圖檔并下載下傳儲存