教你如何用nodejs爬掘金(一)

木魚木心發表於2018-04-14

前言,此文章僅作教學用途,如果有人拿去幹別的事情,我概不負責,如果該文章侵害到了掘金社群的利益,請膜法小編立刻聯絡我刪除.

這是我在掘金的第一篇文章,遂想寫個爬蟲教程吧,目標就是掘金,嘿嘿

本文用到的三個工具為

  • cheerio:jQuery語法,幫助你在非瀏覽器環境下解析網頁用的
  • qs 序列化成url的查詢字串,(不知道說沒說對...)例:{a:1,b2} => a=1&b=2
  • request 一個封裝好的好用的請求庫,我自己把它promise化了一下

全部程式碼見github

開始我是嘗試直接請求掘金首頁,然後用cheerio解析,然後拿到網頁繼續幹活的。。可是事情並沒有這麼簡單,通過這個方法爬取的網頁跟我們正常瀏覽的首頁不一樣(有可能是我哪姿勢不對) 沒辦法,只能從介面出發了

首先開啟網頁版掘金, 然後開啟chrome的network,檢視相關請求

教你如何用nodejs爬掘金(一)
咦!recommend?推薦?好了,進去一看,果然是首頁熱門文章,但是。。。
教你如何用nodejs爬掘金(一)
請求引數suid是什麼?檢視請求呼叫堆疊,,再看原始碼,emmmm 原始碼已經被混淆壓縮了
教你如何用nodejs爬掘金(一)

這可怎麼辦?我沒有登陸 檢視完所有請求響應都沒看到跟suid有關的,這可咋整?

直接進入請求網址,再更改suid,發現隨便更改都可以得到相應 但是。。。這並沒有什麼用啊!就10條資訊還需要爬嗎?

沒辦法了,只能老套路了。先登陸再說

為了防止頁面跳轉後登陸請求消失,需要先勾選Preserve log,使頁面跳轉後前面的請求不會消失

差點忘打碼了,qwq

模擬登陸

教你如何用nodejs爬掘金(一)
我是使用郵箱註冊的,可能使用其它賬號註冊的介面會不一樣

  let data = await request.create({
    url: 'https://juejin.im/auth/type/email',
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({email: '155com', password: 'cfdsd.'}) //賬號密碼
  });
複製程式碼

直接一個請求搞定,得到如下相應,問題的關鍵就在於cookie

教你如何用nodejs爬掘金(一)

好了,接下來該找介面了,點選最新,發現network多了下面這個請求,其響應資料就是最新板塊的文章

教你如何用nodejs爬掘金(一)
相關引數有來源,裝置id,使用者id,token等,其中最重要的就是token,id什麼的隨便改兩個字元好像也沒問題,但是token錯了它會報illegal token,請求方法錯了,就算引數對了也會報missing src。

當你看到token的時候,你會發現,哪都找不到這個資料

但是你仔細看第5張圖的時候,你會發現這auth後面這串字元很像base64編碼

獲取token

開啟相關網站,嘗試解碼

教你如何用nodejs爬掘金(一)

答案呼之欲出啊,最重要的三個引數全在這了,那麼問題來了,node如何解析base64編碼呢?

一行程式碼解決,buffer物件本身提供了base64的解碼功能,最後呼叫toString方法,轉成字串,最後parse得到物件

buffer用法淺析nodejs的buffer類

  const cookie = data.headers['set-cookie'];
  const encodeToken = cookie[0]
    .split(';')[0]
    .split('=')[1];
  const decodeToken = JSON.parse(new Buffer(encodeToken, 'base64').toString())
複製程式碼

有了token,你就可以隨心所欲的爬了,爬圖片?主題?標題?文章內容?完全o98k

爬圖片

async function getPictureUrl(request, url) {
  let data = await request.get({ url });
  console.log(data.body.match(/(https:\/\/user-gold-cdn).+?\/ignore-error\/1/g)); //匹配出圖片url,這裡掘金使用了cdn來儲存圖片
}
複製程式碼

爬最新文章

async function getTopics(request, typeKey) {
  const { token, clientId, userId } = require('./user.json');
  const querystring = qs.stringify({
    src: 'web',
    uid: userId,
    device_id: clientId,
    token: token,
    limit: 20,
    category: 'all',
    recomment: 1
  });
  const types = {
    timeline: 'get_entry_by_timeline', //最新
    comment: 'get_entry_by_comment',   //評論
    rank: 'get_entry_by_rank'          //熱門
  };
  const data = await request.get({
    url: `https://timeline-merger-ms.juejin.im/v1/${types[typeKey]}?${querystring}`,
    headers: {
      host: 'timeline-merger-ms.juejin.im',
      referer: 'https://juejin.im/timeline?sort=comment'
    }
  });
  const body = data.body;
  if (body.s !== 1) {     //出錯時清空資訊
    fs.writeFileSync('./user.json', JSON.stringify({}));
    throw { type: 'token', message: body.m };
  } else {
    return body.d.entrylist;
  }
}
複製程式碼

簡單的處理錯誤

try {
  (async function() {
    const request = new Riven();
    request.setDefaultOptions({
      headers: {
        Cookie:
          'gr_user_id=44868117-2a80-49e8-ba2b-2acd2a77a887; ab={}; _ga=GA1.2.1234597644.150' +
          '6904166; MEIQIA_EXTRA_TRACK_ID=0uMtBISQ3EoiMICJMjpaZedfTBz; _gid=GA1.2.100579701' +
          '2.1523672771; Hm_lvt_93bbd335a208870aa1f296bcd6842e5e=1521573516,1521573752,1522' +
          '270605,1523672771; gr_session_id_89669d96c88aefbc=d54a635e-cece-4f16-aca4-808ae9' +
          '2ee559; gr_cs1_d54a635e-cece-4f16-aca4-808ae92ee559=objectId%3A5a974f6ef265da4e8' +
          '53d8d52; auth=; auth.sig=25Jg_aucc6SpX1VH8RlCoh6azLU; Hm_lpvt_93bbd335a208870aa1' +
          'f296bcd6842e5e=1523675329; QINGCLOUDELB=165e4274d6090771b096025ed82d52a1ab7e48fb' +
          '3972913efd95d72fe838c4fb|WtFwy|WtFwr'
      }
    }); //設不設定cookie都OK的
    if (!isLogin()) {
      login(request);
    }
    const topics = await getTopics(request, 'commnet');
     for (let i = 0; i < topics.length; ++i) {
      //await getDetailData(request, topics[i].objectId);
      getPictureUrl(request, topics[i].originalUrl);
      await sleep(2000); //偽執行緒掛起
    }
  })();
 process.on('unhandledRejection', error => {
    if (error.type === 'token') {
      login();
    }
    console.log(error.message);
  });
} catch (error) {
  console.log(error.message);
}

複製程式碼

當然,我沒有使用資料庫來儲存資料,這只是教大家爬取原理,到這裡,一個超級簡單的爬蟲就完成了

到最後好像也沒用到cheerio了 ◔ ‸◔?

以上程式碼或言論如有錯誤,還望大家指出

最後,我要吐槽一句編輯器,居然不支援貼上圖片???

相關文章