1. 程式人生 > >Node.js/Python爬取網上漫畫

Node.js/Python爬取網上漫畫

版本 中間 kit ont mic 這一 圖片加載 同步 改變

  某個周日晚上偶然發現了《火星異種》這部漫畫,便在網上在線看了起來。在看的過程中圖片加載很慢,而且有時候還不小心點到廣告,大大延緩了我看的進度。後來想到能不能把先把漫畫全部抓取到本地再去看。

  經過一段時間的研究還是有所收獲:成功在風之動漫網站上抓取了《火星異種》,以及在騰訊動漫上抓取了《海賊王》。一般來說,抓取網頁內容有兩種形式:一是靜態資源,即網站內容在第一時間就全部呈現在網頁上;另一種是動態資源,即網站內容隨後通過用戶交互,如:操作滾動條等,異步的更新。對於第一種形式,直接通過發送 request 請求並解析返回結果即可;對於第二種形式,可以通過 PhantomJS 模擬瀏覽器訪問,並通過寫代碼模擬用戶交互行為,最終拿到數據。

1. 靜態資源抓取 (代碼地址:Node.js、Python)

分析階段:

  1. 打開《火星異種》漫畫首頁 ,發現 “http://www.fzdm.com/manhua/47/” 有數字 “47”,將數字 “47” 改成 “48” 後發現是《吞噬人間》漫畫首頁,於是我們知道了這個數字用來標識不同漫畫

  2. 隨機進入 48 話和 86 話,發現連接分別為“http://www.fzdm.com/manhua/47/48/” 和 “http://www.fzdm.com/manhua/47/86/”,於是我們知道了每一話的連接地址(註:實際上第001話的地址為 “http://www.fzdm.com/manhua/47/001/”,但由於當時已經看到 40 多話了,代碼沒有對這一塊進行特殊處理

  3. 隨機進入 50 話,隨機查看不同頁的 URL 後發現 URL 的形式為 “http://www.fzdm.com/manhua/47/50/index_*.html” 其中 * 和當前頁碼對應

4. 查看每一頁的漫畫圖片元素發現,<img> 元素的 id = "mhpic",因此可以輕松獲取到漫畫圖片的地址

  5. 最後只需要發送請求到漫畫圖片所在的地址,並將圖片數據保存在本地即可

註意事項:

  1. 需要記錄當前已抓取到哪一話,也就是記錄當前抓取的狀態。當需要抓取的內容特別長,不能一次性抓完時,保存上一次抓取的斷點十分必要,同時也是為了在抓取的過程中出現錯誤後不用每次從頭抓取

  2. 最好在抓取之前先檢測改文件是否存在,不存在才發送請求。這是為了當程序中出現bug導致已抓取的頁面不全時,不用再重復抓取已經存在的圖片

  3. 添加重試機制,在一定時間訪問同一個站點次數太多,可能會出現服務拒絕之類的錯誤。調節好訪問頻率,出錯後每隔一段時間進行重試即可。

Bonus:

  在查看網頁源碼的時候發現,當前頁面除了加載當前頁的漫畫圖片資源外,還會加載下一話的圖片資源,並在當前頁隱藏。這樣查看下一頁的時候就能直接從緩存中讀取數據。

2. 動態資源抓取 (代碼地址:Node.js)

分析階段:

  1. 打開《海賊王》漫畫首頁,隨機打開 857 和 858 話,地址分別為 “http://ac.qq.com/ComicView/index/id/505430/cid/874” 和 “http://ac.qq.com/ComicView/index/id/505430/cid/875”。發現雖然 URL 是連貫的,但和當前話數字對應不上。打開第一話發現地址是 “http://ac.qq.com/ComicView/index/id/505430/cid/1”,於是猜測中間有些 URL 可能失效了,需要再代碼裏做好容錯 (無論在什麽情況下最好都做好容錯)。

  2. 隨機打開 857 話,發現漫畫圖片是隨著往下滾動動態加載。查看網頁源碼發現,未加載漫畫圖片的地方是用地址為“http://ac.gtimg.com/media/images/pixel.gif”的圖片占位。於是不能簡單的通過單次請求來獲取漫畫圖片地址。需要使用 PhantomJS PhantomJS for Node等工具來模擬瀏覽器訪問,並通過代碼模擬用戶交互行為,最終獲取數據

  3. 通過 setTimeout 改變 scrollTop 來模擬下拉滾動條,滾動到頁面底部後,查看頁面有效 img 標簽的數量看是否等於當前話的頁數,否的話繼續模擬用戶從頭下拉滾動條。

註意事項:

  1. PhantomJS 代碼是在隔離沙箱中運行,不能使用 Node 代碼環境下的全局變量之類的,可以通過 “onConsoleMessage” 事件來與 Node 環境交互。

Python 和 Node 對比使用體驗

  由於對 Node 比較熟,所以是先寫好 Node 版本,有時間的話在用 Python 寫一遍。在寫的過程中發現,Node 的回調寫法用在這樣的場景不太方便,但如果用上 ES7 的 Async/Await 的話,寫起來就方便很多了;但用 Node 的優勢是解析網頁數據十分方便,就像在瀏覽器端進行 DOM 操作一樣。使用 Python 的優勢是同步的寫法,缺點是解析網頁數據不方便。

  雖然 Node 的異步寫法用在這種場景下很不方便,但也正因為異步的關系,可以用一個進程一次性抓取好幾個不同的漫畫,實現類似多線程的效果。

Node.js/Python爬取網上漫畫