TODO-4:nodejs 實現一部漫畫爬取

很早之前一直很喜歡聽一些有聲評書,當時爲了找這些音頻也是煞費苦心。但是找的過程中也發現,我每次下載音頻的時候都是在重複同樣的操作,所以也想過是不是可以用程序實現。那時候並沒有很強的意念去實現是這個工具,所以只是停留在了想的層面。

前不久無意發現一部感覺不錯的動漫《鎮魂街》,看了動畫片之後覺得不過癮,又發現官方的漫畫版還挺有看頭的,於是又從頭開始後擼漫畫,但是發現一頁一頁的翻翻找找好累,所以又想着把這些圖片一次性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;
            });
        });
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章