天天看點

【Python爬蟲3】在下載下傳的本地緩存做爬蟲下載下傳緩存1為連結爬蟲添加緩存支援2磁盤緩存3資料庫緩存

<a href="#%E4%B8%8B%E8%BD%BD%E7%BC%93%E5%AD%98">下載下傳緩存</a>

<a href="#1%E4%B8%BA%E9%93%BE%E6%8E%A5%E7%88%AC%E8%99%AB%E6%B7%BB%E5%8A%A0%E7%BC%93%E5%AD%98%E6%94%AF%E6%8C%81">1為連結爬蟲添加緩存支援</a>

<a href="#2%E7%A3%81%E7%9B%98%E7%BC%93%E5%AD%98">2磁盤緩存</a>

<a href="#21%E7%94%A8%E7%A3%81%E7%9B%98%E7%BC%93%E5%AD%98%E7%9A%84%E5%AE%9E%E7%8E%B0">1用磁盤緩存的實作</a>

<a href="#22%E7%BC%93%E5%AD%98%E6%B5%8B%E8%AF%95">2緩存測試</a>

<a href="#23%E8%8A%82%E7%9C%81%E7%A3%81%E7%9B%98%E7%A9%BA%E9%97%B4">3節省磁盤空間</a>

<a href="#24%E6%B8%85%E7%90%86%E8%BF%87%E6%9C%9F%E6%95%B0%E6%8D%AE">4清理過期資料</a>

<a href="#25%E7%94%A8%E7%A3%81%E7%9B%98%E7%BC%93%E5%AD%98%E7%9A%84%E7%BC%BA%E7%82%B9">5用磁盤緩存的缺點</a>

<a href="#3%E6%95%B0%E6%8D%AE%E5%BA%93%E7%BC%93%E5%AD%98">3資料庫緩存</a>

<a href="#31nosql%E6%98%AF%E4%BB%80%E4%B9%88">1NoSQL是什麼</a>

<a href="#32%E5%AE%89%E8%A3%85mongodb">2安裝MongoDB</a>

<a href="#33mongodb%E6%A6%82%E8%BF%B0">3MongoDB概述</a>

<a href="#34mongodb%E7%BC%93%E5%AD%98%E5%AE%9E%E7%8E%B0">4MongoDB緩存實作</a>

<a href="#35%E5%8E%8B%E7%BC%A9%E5%AD%98%E5%82%A8">5壓縮存儲</a>

<a href="#36%E7%BC%93%E5%AD%98%E6%B5%8B%E8%AF%95">6緩存測試</a>

<a href="#37mongodb%E7%BC%93%E5%AD%98%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%81">7MongoDB緩存完整代碼</a>

上篇文章,我們學習了如何提取網頁中的資料,以及将提取結果存到表格中。如果我們還想提取另一字段,則需要重新再下載下傳整個網頁,這對我們這個小型的示例網站問題不大,但對于數百萬個網頁的網站而言來說就要消耗幾個星期的時間。是以,我們可以先對網頁進行緩存,就使得每個網頁隻下載下傳一次。

我們将downloader重構一類,這樣參數隻需在構造方法中設定一次,就能在後續多次複用,在URL下載下傳之前進行緩存檢查,并把限速功能移到函數内部。

在Downloader類的call特殊方法實作了下載下傳前先檢查緩存,如果已經定義該URL緩存則再檢查下載下傳中是否遇到了服務端錯誤,如果都沒問題表明緩存結果可用,否則都需要正常下載下傳該URL存到緩存中。

downloader方法傳回添加了HTTP狀态碼,以便緩存中存儲錯誤機校驗。如果不需要限速或緩存的話,你可以直接調用該方法,這樣就不會通過call方法調用了。

為了支援緩存功能,連結爬蟲代碼也需用一些微調,包括添加cache參數、移除限速以及将download函數替換為新的類。

現在,這個支援緩存的網絡爬蟲的基本架構已經準備好了,下面就要開始建構實際的緩存功能了。

作業系統

檔案系統

非法檔案名字元

檔案名最大長度

Linux

Ext3/Ext4

<code>/</code>和<code>\0</code>

255個位元組

OS X

HFS Plus

<code>:</code>和<code>\0</code>

255個UTF-16編碼單元

Windows

NTFS

<code>\</code>、<code>/</code>、<code>?</code>、<code>:</code>、<code>*</code>、<code>"</code>、<code>&gt;</code>、<code>&lt;</code>和`

`

為了保證在不同檔案系統中,我們的檔案路徑都是安全的,就需要把除數字、字母和基本符号的其他字元替換為下劃線。

此外,檔案名及其目錄長度需要限制在255個字元以内。

還有一種邊界情況,就是URL以斜杠結尾。這樣分割URL後就會造成一個非法的檔案名。例如:

對于第一個URL可以在後面添加index.html作為檔案名,是以可以把index作為目錄名,1為子目錄名,index.html為檔案名。

現在可以把URL到目錄和檔案名完整映射邏輯結合起來,就形成了磁盤緩存的主要部分。該構造方法傳入了用于設定緩存位置的參數,然後在url_to_path方法中應用了前面讨論的檔案名限制。

現在我們還缺少根據檔案名存取資料的方法,就是Downloader類<code>result=cache[url]</code>和<code>cache[url]=result</code>的接口方法:<code>__getitem__()</code>和<code>__setitem__()</code>兩個特殊方法。

在<code>__setitem__()</code>中,我們使用url_to_path()方法将URL映射為安全檔案名,在必要情況下還需要建立目錄。這裡使用的pickle子產品會把輸入轉化為字元串(序列化),然後儲存到磁盤中。

在<code>__getitem__()</code>中,還是先用url_to_path()方法将URL映射為安全檔案名。然後檢查檔案是否存在,如果存在則加載内容,并執行反序列化,恢複其原始資料類型;如果不存在,則說明緩存中還沒有該URL的資料,此時會抛出KeyError異常。

可以在python指令前加<code>time</code>計時。我們可以發現,如果是在本地伺服器的網站,當緩存為空時爬蟲實際耗時<code>0m58.710s</code>,第二次運作全部從緩存讀取花了<code>0m0.221s</code>,快了<code>265</code>多倍。如果是爬取遠端伺服器的網站的資料時,将會耗更多時間。

為節省緩存占用空間,我們可以對下載下傳的HTML檔案進行壓縮處理,使用zlib壓縮序列化字元串即可。

從磁盤加載後解壓的代碼如下:

壓縮所有網頁之後,緩存占用大小<code>2.8 MB</code>下降到<code>821.2 KB</code>,耗時略有增加。

本節中,我們将為緩存資料添加過期時間,以便爬蟲知道何時需要重新下載下傳網頁。在構造方法中,我們使用timedelta對象将預設過期時間設定為30天,在<code>__set__</code>方法中把目前時間戳儲存在序列化資料中,在<code>__get__</code>方法中對比目前時間和緩存時間,檢查是否過期。

為了測試時間功能,我們可以将其縮短為5秒,如下操作:

由于受制于檔案系統的限制,之前我們将URL映射為安全檔案名,然而這樣又會引發一些問題:

- 有些URL會被映射為相同的檔案名。比如URL:<code>.../count.asp?a+b</code>,<code>.../count.asp?a*b</code>。

- URL截斷255個字元的檔案名也可能相同。因為URL可以超過2000下字元。

使用URL哈希值為檔案名可以帶來一定的改善。這樣也有一些問題:

- 每個卷和每個目錄下的檔案數量是有限制的。FAT32檔案系統每個目錄的最大檔案數65535,但可以分割到不同目錄下。

- 檔案系統可存儲的檔案總數也是有限的。ext4分區目前支援略多于1500萬個檔案,而一個大型網站往往擁有超過1億個網頁。

要想避免這些問題,我們需要把多個緩存網頁合并到一個檔案中,并使用類似B+樹的算法進行索引。但我們不會自己實作這種算法,而是在下一節中介紹已實作這類算法的資料庫。

爬取時,我們可能需要緩存大量資料,但又無須任何複雜的連接配接操作,是以我們将選用NoSQL資料庫,這種資料庫比傳統的關系型資料庫更容易擴充。在本節中,我們将選用目前非常流行的MongoDB作為緩存資料庫。

- 列資料存儲(如HBase);

- 鍵值對存儲(如Redis);

- 圖形資料庫(如Neo4j);

- 面向文檔的資料庫(如MongoDB)。

檢測安裝是否成功,在本地啟動MongoDB伺服器:

然後,在Python中,使用MongoDB的預設端口嘗試連接配接MongoDB:

下面是MongoDB示例代碼:

當插入同一條記錄時,MongoDB會欣然接受并執行這次操作,但通過查找發現記錄沒更新。

為了存儲最新的記錄,并避免重複記錄,我們将ID設定為URL,并執行<code>upsert</code>操作。該操作表示當記錄存在時則更新記錄,否則插入新記錄。

現在我們已經準備好建立基于MongoDB的緩存了,這裡使用了和之前的DiskCache類相同的接口。我們在下面構造方法中建立了<code>timestamp</code>索引,在達到給定的時間戳之後,MongoDB的這一便捷功能可以自動過期删除記錄。

下面我們來測試一下這個MongoCache類,我們用預設0時間間隔<code>timedelta()</code>對象進行測試,此時記錄建立後應該會馬上會被删除,但實際卻沒有。這是因為MongoDB運作機制造成的,MongoDB背景運作了一個每分鐘檢查一次過期記錄的任務。是以我們可以再等一分鐘,就會發現緩存過期機制已經運作成功了。

可以看出,用資料庫緩存的讀取時間是磁盤緩存的兩倍,但成功地避免了磁盤緩存的缺點。

Wu_Being 部落格聲明:本人部落格歡迎轉載,請标明部落格原文和原連結!謝謝!

【Python爬蟲3】在下載下傳的本地緩存做爬蟲下載下傳緩存1為連結爬蟲添加緩存支援2磁盤緩存3資料庫緩存

如果你看完這篇博文,覺得對你有幫助,并且願意付贊助費,那麼我會更有動力寫下去。