很早之前一直很喜歡聽一些有聲評書,當時爲了找這些音頻也是煞費苦心。但是找的過程中也發現,我每次下載音頻的時候都是在重複同樣的操作,所以也想過是不是可以用程序實現。那時候並沒有很強的意念去實現是這個工具,所以只是停留在了想的層面。
前不久無意發現一部感覺不錯的動漫《鎮魂街》,看了動畫片之後覺得不過癮,又發現官方的漫畫版還挺有看頭的,於是又從頭開始後擼漫畫,但是發現一頁一頁的翻翻找找好累,所以又想着把這些圖片一次性down下來該多爽。
正巧手頭有個ng2的項目,於是藉助這個框架開始嘗試爬取這部漫畫,沒有廣告,沒有連接,簡單粗暴直接看圖。
抓取網頁的幾個必要工具先羅列一下:
1:superagent github 地址 代替客戶端發起請求;
2:cheerio github地址 解析html
3:fs node 的文件系統模塊
首先找個穩定點的網站用來抓取,不然剛研究好怎麼解析這個網站下的頁面,結果頁面不穩定,白費力氣。
解析的思路一定是順序的,首先要把一部漫畫的章節找出來,然後根據每個章節下載裏面的圖片。
下面說說我在抓取的過程遇到的幾個小問題:
1:編碼問題;
默認情況下superagent是utf-8的編碼,但是如果遇到網站的編碼不是utf-8就會出現抓取下來的頁面都是亂碼,自然就不好解析了。
所以出現這種情況的時候,可以在發起請求的時候添加編碼設置,這就涉及到了第四個必要工具:superagent-charset,github地址,防止亂碼用的。
2:請求頭的問題;
一般這種網站都會有防盜鏈的措施,所以如果隨意請求,很快就會被反套路,所以最安全的方案就是用瀏覽器打開需要抓取的頁面,找到相應的網絡請求頭,全copy下來,不過要特別注意一下有的Referer這個字段是固定的連接,但是有的是根據章節,每個章節不同。
3:請求‘順序’還是‘併發’
最初使用併發的形式抓取,但是會出現抓取的圖片不全的現象。這種情況還不是很清楚什麼原因導致的。不過轉爲順序並且把每次請求都間隔了一定時間,儘量的模仿實際人工請求,沒有出現抓取不全的現象。
下面是主要代碼邏輯:
用來統一設置請求頭的方法:
function makeReuest(option){
return request.get(option.url).charset('gbk')
.set('Accept','text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
// .set('Accept-Encoding','gzip, deflate, sdch')
.set('Accept-Language','zh-CN,zh;q=0.8,en;q=0.6')
.set('Cache-Control','no-cache')
.set('Connection','keep-alive')
.set('Cookie',option.cookie)
.set('Host',option.host)
.set('Pragma','no-cache')
.set('Referer',option.referer)
// .set('Upgrade-Insecure-Requests','1')
.set('User-Agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36')
.catch(option.cb)
;
}
所有callback轉爲promise形式
module.exports = function (func, option = {}) {
return (...arg) => new Promise((resolve, reject) => {
return func.apply(option.context, [...arg, (err, ...data) => {
if (err) {
return reject(err);
}
if (option.multiArg) {
return resolve(data)
}
return resolve(data[0]);
}]);
})
}
寫文件邏輯:
const writeFileAsync = promisify(fs.writeFile);
const mkdirAsync = promisify(fs.mkdir);
writeFileAsync(imgPath, _picRes.body)
.then(function () {
console.log('write OK:', imgPath)
})
.catch(function () {
console.log('write err:', imgPath)
mkdirAsync(path)
.then(function () {
return writeFileAsync(imgPath, _picRes.body)
})
.then(function () {
console.log('write file retry finished:', imgPath)
})
.catch(function (err) {
console.log('retry err', err)
throw err;
});
});