標籤:

如何獲取一個知乎問題下所有的圖片

動機

最近看知乎很久,因為關注了攝影話題,總有各種

用無人機自拍是一種怎樣的新體驗?

你有哪些拍的很美並願意分享的照片?

雖然我也喜歡拍照,但是照片真的不如別人拍的好看啊,受限於自己用的是13寸電腦,看知乎有個bug,那個收起按鈕永遠不知道收起的哪一個,所以就想,有沒有辦法可以把圖都保存到本地呢?

(廢話當然**有啊)

語言選擇

可以爬蟲的語言有很多,現在搜索引擎中排行最熱門的兩個是Java爬蟲和python爬蟲

因為第一份實習工作是Java相關,導致我對java有了一種抵觸情緒,後來就一直考慮python爬蟲,但是覺得使用python還是使用了其他語言,為什麼不能用Javascript直接來寫爬蟲呢?

Nodejs爬蟲

現在是一枚前端er,主要在寫JS方面的東西,就開始查可不可以用nodejs來寫一個爬蟲,發現已經有很多人用nodejs來做爬蟲,並且效果都還不錯。

確定語言使用Node.js之後,工欲善其事必先利其器,那麼選擇一個合適的編譯器就變得尤為重要,現在流行的主要是Sublime, Atom 和 Webstorm(就前端而言WS還是蠻火的,當然JetBrain家都系都還不錯哈)這我選擇了(並推薦)Webstorm

Node.js和python一樣,都有很多現成的第三方庫,而安裝node的時候,會自動幫我們安裝npm,npm相比python的pip來講,就是限制少,不會受限於p2p3等問題的困擾,那麼我們都需要引入哪些第三方庫呢

var fs = require(fs);

fs包用來對系統目錄進來讀寫工作,用來將將文件寫入我的本地路徑

var path = require(path);

path包用來解析路徑,包括給抓到的文件重新命名等

var request = require(request);

用來發送HTTP請求

var cheerio = require(cheerio);

Cherrio 可以在伺服器段想使用Jquery的方式操作Dom結構

var https = require(https);

第一步

我們先看請求回來,會是什麼樣的東西

var request = require(request);var reqUrl = https://www.zhihu.com/;request(reqUrl, function (error, response, body) { if (!error && response.statusCode == 200) { console.log(body); }})

這樣請求回來的body中就是我們所請求網頁的代碼,也就是我們再網頁右鍵(Inspect)的控制台中的Element看到的代碼,但是平時工作用的都是jQuery,如何能用jQuery的選擇器來選擇標籤,進行爬蟲呢?

第二步

既然我們想使用jQuyer的選擇器,那麼數據請求回來之後,我們需要對數據進行處理,從而達到我們的目的,更簡單的選取我們需要的內容。我們使用cherrio對取回的數據進行處理

var $ = cheerio.load(data);

這一步之後,我們可以直接使用jQuery的選擇器來進行查找我們需要的內容,具體cheerio的操作可以看一下cheerio的API,但就選擇器來言,和jQuery是一樣的。這就是cheerio的選擇器

$(selectior,[context],[root])

第三步

我們需要確定我們所希望選取的元素的位置,在這裡我們想爬取知乎某一問題下的圖片,那麼我們可以打開控制台,然後找到某一條答案下最外層的div,然後逐級向內推。

var imgs = $(.zm-item-answer).find(.zm-item-rich-text).find(.zm-editable-content).find(img).toArray();

這裡是我已經選擇好的img的地址和路徑,在這個路徑下可以選擇該答案下所有的圖片

第四步

取回這條數據之後,返回這個數據的長度,並列印,在這裡我測試的時候是喜歡列印出來,來確定是否執行成功或取回內容是不是達到預期。

console.log(imgs.length())

最早在做爬蟲的時候,我直接列印出了imgs中的內容,發現很難進行判斷圖片再哪一個位置,直到我使用的Webstorm,在console這一條之前打上斷點,就可以看到imgs中包含哪些數據。

從圖中可以看到imgs是一個Array,他的長度是122,我們取第一個來看,即使是一個不經常寫代碼的人,看到這個src後面跟的地址,也會知道,這個圖片地址肯定是src提供的,那我們就取

obj = imgs[j].attribs;

這一步為什麼沒有直接取src呢,那我們不妨多看幾張圖

這是第二張圖,我們發現,這裡面如果你取src就會造成路徑失敗取不到圖片,所以在代碼中我加了一層驗證

var url = imgs[j].attribs[data-actualsrc];if (url == undefined && obj[src]) { url = obj[src]; console.log(j + successful with src + url);} else if (url == undefined && obj[data-original]) { url = obj[data-original]; console.log(j + successful with data-original + url);} else { url = imgs[j].attribs[data-actualsrc];}

這裡面,我首先在obj之後定義了一個默認的url是取data-actualsrc,而如果我取不到這個值,就去取src試試,如果再不取不到,我就用data-original試試,其實這個else沒有用,看到這裡你會問,如果三個都沒有,你這程序不就報錯了,按理說,如果三個都沒有我應該在else中寫一個continue之類的,保證不會在下一步報錯,是的,正確來說,真的應該這麼做

但是我頭硬啊!

第五步

我們已經取到了圖片地址,下一步我們是不是該把文件保存到本地了。

不是。

我們的文件還沒有命名,這裡就用到了開頭我們require的path模塊了,可以使用path模塊中的basename來解析文件名,這樣我們就不用一個個去命名了,當然也不允許你這樣。

function parseUrlForFileName(address) {var filename = path.basename(address); return filename;}

完成文件命名後,我們就可以使用fs模塊將圖片保存到本地了

function downloadImg(uri,fileName,callback) { request.head(uri, function(err,res,body) {if(err) { console.log(err: + err); return false; } request(uri).pipe(fs.createWriteStream(img/ + fileName)).on(close, callback); });}

這個方法中,我們需要傳入url,想要的文件名,和一個回調函數。有了url我們就可以下載圖片了,這裡我們使用request head的方法來下載,然後再調用fs模塊中createWriteStream來寫入到本地目錄

最後

這樣一個爬蟲就完成了,只需要改變url地址,就可以獲取不同的圖片,唯一的困惑時,我imgs的長度是120,只能取回60張照片,我把imgs的保存成一個數組也發現確每一條都會取兩次,希望有大神看到的時候噴一下原因,我一定耐心接受。

當然你們以後如果用這個東西去爬輪子哥點贊導致知乎把這個方法堵死了,你們要負責任的!!!!!!!

最後附一個項目的github地址吧spider-zhihu

拜個早年


推薦閱讀:

拉勾網職位信息爬蟲練習
使用python爬豆瓣書單
網易雲音樂-聽見好時光
Python3安裝scrapy相關問題(windows平台)
數據分析案例:51job網站職位信息探索

TAG:爬蟲 |