Python爬蟲-用Scrapy框架實現漫畫的爬取

發表於2016-12-30

Python爬蟲-用Scrapy框架實現漫畫的爬取

在之前一篇抓取漫畫圖片的文章裡,通過實現一個簡單的Python程式,遍歷所有漫畫的url,對請求所返回的html原始碼進行正規表示式分析,來提取到需要的資料。

本篇文章,通過 scrapy 框架來實現相同的功能。scrapy 是一個為了爬取網站資料,提取結構性資料而編寫的應用框架。關於框架使用的更多詳情可瀏覽官方文件,本篇文章展示的是爬取漫畫圖片的大體實現過程。

scrapy環境配置

安裝

首先是 scrapy 的安裝,博主用的是Mac系統,直接執行命令列:

對於html節點資訊的提取使用了 Beautiful Soup 庫,大概的用法可見之前的一篇文章,直接通過命令安裝:

對於目標網頁的 Beautiful Soup 物件初始化需要用到 html5lib 直譯器,安裝的命令:

安裝完成後,直接在命令列執行命令:

可以看到如下輸出結果,這時候證明scrapy安裝完成了。

專案建立

通過命令列在當前路徑下建立一個名為 Comics 的專案

建立完成後,當前目錄下出現對應的專案資料夾,可以看到生成的Comics檔案結構為:

Ps. 列印當前檔案結構命令為:

每個檔案對應的具體功能可查閱官方文件,本篇實現對這些檔案涉及不多,所以按下不表。

建立Spider類

建立一個用來實現具體爬取功能的類,我們所有的處理實現都會在這個類中進行,它必須為 scrapy.Spider 的子類。

Comics/spiders 檔案路徑下建立 comics.py 檔案。

comics.py 的具體實現:

自定義的類為scrapy.Spider的子類,其中的name屬性為該爬蟲的唯一標識,作為scrapy爬取命令的引數。其他方法的屬性後續再解釋。

執行

建立好自定義的類後,切換到Comics路徑下,執行命令,啟動爬蟲任務開始爬取網頁。

列印的結果為爬蟲執行過程中的資訊,和目標爬取網頁的html原始碼。

此時,一個基本的爬蟲建立完成了,下面是具體過程的實現。

爬取漫畫圖片

起始地址

爬蟲的起始地址為:

我們主要的關注點在於頁面中間的漫畫列表,列表下方有顯示頁數的控制元件。如下圖所示

Python爬蟲-用Scrapy框架實現漫畫的爬取

爬蟲的主要任務是爬取列表中每一部漫畫的圖片,爬取完當前頁後,進入下一頁漫畫列表繼續爬取漫畫,依次不斷迴圈直至所有漫畫爬取完畢。

起始地址的url我們放在了start_requests函式的urls陣列中。其中start_requests是過載了父類的方法,爬蟲任務開始時會執行到這個方法。

start_requests方法中主要的執行在這一行程式碼:請求指定的url,請求完成後呼叫對應的回撥函式self.parse

對於之前的程式碼其實還有另一種實現方式:

start_urls是框架中提供的屬性,為一個包含目標網頁url的陣列,設定了start_urls的值後,不需要過載start_requests方法,爬蟲也會依次爬取start_urls中的地址,並在請求完成後自動呼叫parse作為回撥方法。

不過為了在過程中方便調式其它的回撥函式,demo中還是使用了前一種實現方式。

爬取漫畫url

從起始網頁開始,首先我們要爬取到每一部漫畫的url。

當前頁漫畫列表

起始頁為漫畫列表的第一頁,我們要從當前頁中提取出所需資訊,動過實現回撥parse方法。

在開頭匯入BeautifulSoup

請求返回的html原始碼用來給BeautifulSoup初始化。

初始化指定了html5lib直譯器,若沒安裝這裡會報錯。BeautifulSoup初始化時若不提供指定直譯器,則會自動使用自認為匹配的最佳直譯器,這裡有個坑,對於目標網頁的原始碼使用預設最佳直譯器為lxml,此時解析出的結果會有問題,而導致無法進行接下來的資料提取。所以當發現有時候提取結果又問題時,列印soup看看是否正確。

檢視html原始碼可知,頁面中顯示漫畫列表的部分為類名為listconul標籤,通過listcon類能唯一確認對應的標籤

Python爬蟲-用Scrapy框架實現漫畫的爬取

提取包含漫畫列表的標籤

上面的find方法意為尋找classlistconul標籤,返回的是對應標籤的所有內容。

在列表標籤中查詢所有擁有href屬性的a標籤,這些a標籤即為每部漫畫對應的資訊。

然後將每部漫畫的href屬性合成完整能訪問的url地址,儲存在一個陣列中。

此時comics_url_list陣列即包含當前頁每部漫畫的url。

下一頁列表

看到列表下方的選擇頁控制元件,我們可以通過這個地方來獲取到下一頁的url。

Python爬蟲-用Scrapy框架實現漫畫的爬取

獲取選擇頁標籤中,所有包含href屬性的a標籤

這部分原始碼如下圖,可看到,所有的a標籤中,倒數第一個代表末頁的url,倒數第二個代表下一頁的url,因此,我們可以通過取page_a_list陣列中倒數第二個元素來獲取到下一頁的url。

Python爬蟲-用Scrapy框架實現漫畫的爬取

但這裡需要注意的是,若當前為最後一頁時,不需要再取下一頁。那麼如何判斷當前頁是否是最後一頁呢?

可以通過select控制元件來判斷。通過原始碼可以判斷,當前頁對應的option標籤會具有selected屬性,下圖為當前頁為第一頁

Python爬蟲-用Scrapy框架實現漫畫的爬取

下圖為當前頁為最後一頁

Python爬蟲-用Scrapy框架實現漫畫的爬取

通過當前頁數與最後一頁頁數做對比,若相同則說明當前頁為最後一頁。

當前不為最後一頁,則繼續對下一頁做相同的處理,請求依然通過回撥parse方法做處理

通過同樣的方式依次處理每一頁,直到所有頁處理完成。

爬取漫畫圖片

parse方法中提取到當前頁的所有漫畫url時,就可以開始對每部漫畫進行處理。

在獲取到comics_url_list陣列的下方加上下面程式碼:

對每部漫畫的url進行請求,回撥處理方法為self.comics_parsecomics_parse方法用來處理每部漫畫,下面為具體實現。

當前頁圖片

首相將請求返回的原始碼構造一個BeautifulSoup,和前面基本一致

提取選擇頁控制元件標籤,頁面顯示和原始碼如下所示

Python爬蟲-用Scrapy框架實現漫畫的爬取

Python爬蟲-用Scrapy框架實現漫畫的爬取

提取classpagelistul標籤

檢視原始碼可以看到當前頁的li標籤的class屬性thisclass,以此獲取到當前頁頁數

當前頁圖片的標籤和對應原始碼

Python爬蟲-用Scrapy框架實現漫畫的爬取

Python爬蟲-用Scrapy框架實現漫畫的爬取

獲取當前頁圖片的url,以及漫畫的標題。漫畫標題之後用來作為儲存對應漫畫的資料夾名稱。

儲存到本地

當提取到圖片url時,便可通過url請求圖片並儲存到本地

定義了一個專門用來儲存圖片的方法save_img,具體完整實現如下

函式主要用到3個引數,當前圖片的頁數,漫畫的名稱,圖片的url。

圖片會儲存在以漫畫名稱命名的資料夾中,若不存在對應資料夾,則建立一個,一般在獲取第一張圖時需要自主建立一個資料夾。

document為本地指定的資料夾,可自定義。

每張圖片以頁數.jpg的格式命名,若本地已存在同名圖片則不再進行重新下載,一般用在反覆開始任務的情況下進行判斷以避免對已存在圖片進行重複請求。

請求返回的圖片資料是被壓縮過的,可以通過response.info().get('Content-Encoding')的型別來進行判斷。壓縮過的圖片要先經過zlib.decompress解壓再儲存到本地,否則圖片打不開。

大體實現思路如上,程式碼中也附上註釋了。

下一頁圖片

和在漫畫列表介面中的處理方式類似,在漫畫頁面中我們也需要不斷獲取下一頁的圖片,不斷的遍歷直至最後一頁。

Python爬蟲-用Scrapy框架實現漫畫的爬取

當下一頁標籤的href屬性為#時為漫畫的最後一頁

若當前為最後一頁,則該部漫畫遍歷完成,否則繼續通過相同方式處理下一頁

執行結果

大體的實現基本完成,執行起來,可以看到控制檯列印情況

Python爬蟲-用Scrapy框架實現漫畫的爬取

本地資料夾儲存到的圖片

Python爬蟲-用Scrapy框架實現漫畫的爬取

scrapy框架執行的時候使用了多執行緒,能夠看到多部漫畫是同時進行爬取的。

目標網站資源伺服器感覺比較慢,會經常出現請求超時的情況。跑的時候請耐心等待。:)

最後

本文介紹的只是scrapy框架非常基本的用法,還有各種很細節的特性配置,如使用FilesPipelineImagesPipeline來儲存下載的檔案或者圖片;框架本身自帶了個XPath類用來對網頁資訊進行提取,這個的效率要比BeautifulSoup高;也可以通過專門的item類將爬取的資料結果儲存作為一個類返回。具體請查閱官網。

最後附上完整Demo原始碼

 

相關文章