上一篇博客講解了使用nodejs爬取博客園的博文,這次帶給大家的是下載網路上的圖片。 需要用到的第三方模塊有: superagent superagent-charset (手動指定編碼,解決GBK中文亂碼) cheerio express async (併發控制) 完整的代碼,可以在我的githu ...
上一篇博客講解了使用nodejs爬取博客園的博文,這次帶給大家的是下載網路上的圖片。
需要用到的第三方模塊有:
superagent
superagent-charset (手動指定編碼,解決GBK中文亂碼)
cheerio
express
async (併發控制)
完整的代碼,可以在我的github中可以下載。主要的邏輯邏輯在 netbian.js 中。
以彼岸桌面(http://www.netbian.com/)欄目下的風景壁紙(http://www.netbian.com/fengjing/index.htm)為例進行講解。
1. 分析URL
不難發現:
首頁: 欄目/index.htm
分頁: 欄目/index_具體頁碼.htm
知道這個規律,就可以批量下載壁紙了。
2. 分析壁紙縮略圖,找到對應壁紙的大圖
使用chrome的開發者工具,可以發現,縮略圖列表在 class="list"的div里,a標簽的href屬性的值就是單張壁紙所在的頁面。
部分代碼:
1 request 2 .get(url) 3 .end(function(err, sres){ 4 5 var $ = cheerio.load(sres.text); 6 var pic_url = []; // 中等圖片鏈接數組 7 $('.list ul', 0).find('li').each(function(index, ele){ 8 var ele = $(ele); 9 var href = ele.find('a').eq(0).attr('href'); // 中等圖片鏈接 10 if(href != undefined){ 11 pic_url.push(url_model.resolve(domain, href)); 12 } 13 }); 14 });
3. 以“http://www.netbian.com/desk/17662.htm”繼續分析
打開這個頁面,發現此頁面顯示的壁紙,依舊不是最高的解析度。
點擊“下載壁紙”按鈕里的鏈接,打開新的頁面。
4. 以“http://www.netbian.com/desk/17662-1920x1080.htm”繼續分析
打開這個頁面,我們最終要下載的壁紙,放在一個table裡面。如下圖,http://img.netbian.com/file/2017/0203/bb109369a1f2eb2e30e04a435f2be466.jpg
才是我們最終要下載的圖片的URL(幕後BOSS終於現身了(@ ̄ー ̄@))。
下載圖片的代碼:
request .get(wallpaper_down_url) .end(function(err, img_res){ if(img_res.status == 200){ // 保存圖片內容 fs.writeFile(dir + '/' + wallpaper_down_title + path.extname(path.basename(wallpaper_down_url)), img_res.body, 'binary', function(err){ if(err) console.log(err); }); } });
打開瀏覽器,訪問 http://localhost:1314/fengjing
選擇欄目和頁面,點擊“開始”按鈕:
併發請求伺服器,下載圖片。
完成~
圖片的存放目錄按照 欄目+頁碼 的形式保存。
附上完整的圖片下載的代碼:
1 /** 2 * 下載圖片 3 * @param {[type]} url [圖片URL] 4 * @param {[type]} dir [存儲目錄] 5 * @param {[type]} res [description] 6 * @return {[type]} [description] 7 */ 8 var down_pic = function(url, dir, res){ 9 10 var domain = 'http://www.netbian.com'; // 功能變數名稱 11 12 request 13 .get(url) 14 .end(function(err, sres){ 15 16 var $ = cheerio.load(sres.text); 17 var pic_url = []; // 中等圖片鏈接數組 18 $('.list ul', 0).find('li').each(function(index, ele){ 19 var ele = $(ele); 20 var href = ele.find('a').eq(0).attr('href'); // 中等圖片鏈接 21 if(href != undefined){ 22 pic_url.push(url_model.resolve(domain, href)); 23 } 24 }); 25 26 var count = 0; // 併發計數器 27 var wallpaper = []; // 壁紙數組 28 var fetchPic = function(_pic_url, callback){ 29 30 count++; // 併發加1 31 32 var delay = parseInt((Math.random() * 10000000) % 2000); 33 console.log('現在的併發數是:' + count + ', 正在抓取的圖片的URL是:' + _pic_url + ' 時間是:' + delay + '毫秒'); 34 setTimeout(function(){ 35 // 獲取大圖鏈接 36 request 37 .get(_pic_url) 38 .end(function(err, ares){ 39 var $$ = cheerio.load(ares.text); 40 var pic_down = url_model.resolve(domain, $$('.pic-down').find('a').attr('href')); // 大圖鏈接 41 42 count--; // 併發減1 43 44 // 請求大圖鏈接 45 request 46 .get(pic_down) 47 .charset('gbk') // 設置編碼, 網頁以GBK的方式獲取 48 .end(function(err, pic_res){ 49 50 var $$$ = cheerio.load(pic_res.text); 51 var wallpaper_down_url = $$$('#endimg').find('img').attr('src'); // URL 52 var wallpaper_down_title = $$$('#endimg').find('img').attr('alt'); // title 53 54 // 下載大圖 55 request 56 .get(wallpaper_down_url) 57 .end(function(err, img_res){ 58 if(img_res.status == 200){ 59 // 保存圖片內容 60 fs.writeFile(dir + '/' + wallpaper_down_title + path.extname(path.basename(wallpaper_down_url)), img_res.body, 'binary', function(err){ 61 if(err) console.log(err); 62 }); 63 } 64 }); 65 66 wallpaper.push(wallpaper_down_title + '下載完畢<br />'); 67 }); 68 callback(null, wallpaper); // 返回數據 69 }); 70 }, delay); 71 }; 72 73 // 併發為2,下載壁紙 74 async.mapLimit(pic_url, 2, function(_pic_url, callback){ 75 fetchPic(_pic_url, callback); 76 }, function (err, result){ 77 console.log('success'); 78 res.send(result[0]); // 取下標為0的元素 79 }); 80 }); 81 };
特別需要註意的兩點:
1. “彼岸桌面”網頁的編碼是“GBK”的。而nodejs本身只支持“UTF-8”編碼。這裡我們引入“superagent-charset”模塊,用於處理“GBK”的編碼。
附上github里的一個例子
https://github.com/magicdawn/superagent-charset
2. nodejs是非同步的,同一時間發送大量的請求,有可能被伺服器認為是惡意請求而拒絕。 因此這裡引入“async”模塊,用於併發的處理,使用的方法是:mapLimit。
mapLimit(arr, limit, iterator, callback)
這個方法有4個參數:
第1個參數是數組。
第2個參數是併發請求的數量。
第3個參數是迭代器,通常是一個函數。
第4個參數是併發執行後的回調。
這個方法的作用是將arr中的每個元素同時併發limit次拿給iterator去執行,執行結果傳給最後的callback。
後話
至此,便完成了圖片的下載。
完整的代碼,已經放在github上,歡迎star (☆▽☆)。
文筆有限,才學疏淺,若有不正確的地方,歡迎廣大博友指正。