實踐nightmare-簡單分析sf的小數據

在sf裏我回答完一個問題,總有一個優先操做,那就是取消關注問題,由於當你回答完一個問題,系統默認你關注了這個問題。這對於有點強迫症的我來講,是一種困擾,由於這樣,別人回答了這個問題,系統就會提醒你一次,當一個問題變得熱門,不少人來回答的時候,這諸多提醒通知就讓人太尷尬了。在sf裏,我主張的關注,是自動關注,被動關注啥的,我都不想要。node

廢話那麼一段,因而想着要不寫個腳本收集一下問題的關注數跟回答數的關係,猜猜看,這個被動關注是否真的有必要?或者說,我就看看是否是也有人跟我同樣回答完也取消了關注問題……python

其實這種數據收集,要是有數據庫最好了,but,咱又不是sf的員工……數據庫

思路

  1. 首先在首頁獲取全部問題(貌似顯示80條),但不是全部問題都有回答,因而篩選出有回答數的問題連接編程

  2. 根據上一步獲取的連接,一個個的訪問問題詳情頁,收集主要數據(問題標題、連接、收藏數、回答數、關注數、瀏覽數)segmentfault

  3. 把上一步收集的主要數據作一下記錄和篩選(這裏篩選,我主要是看看 回答數+1>關注數 的問題,提問者默認關注了問題自己)數組

實現

用的是nightmare,須要安裝,其基於electron,安裝過程可能須要安裝python2.promise

nightmare主要是以promise格式來編寫的代碼,因而我封裝了一下一個function,方便後續編程dom

// url 爲頁面連接
// evaluate 爲頁面的植入js運行函數
// thenCbk 則是後續的node做用域下的回調函數,參數接收來自evaluate函數返回的內容
function nightPage(url, evaluate, thenCbk){
  nightmare
    .goto(url)
    .wait('body')
    .evaluate(evaluate)
    .then(thenCbk)
    .catch(function (error) {
      console.error('nightPage failed! url:',url,'\nerror:', error);
    });
}
// 關閉 nightmare
function end(message){
  console.log('ending:'+message);
  nightmare.end().then(function(res){
    console.log('nightmare end!');
    //這是後面的處理數據調用
    staticLog();
  });
}

因而,頁面數據的收集,和後續處理,就取決於evalutethenCbk的邏輯了。electron

先訪問首頁,獲得問題連接集合:ide

function getQuestions(){
    nightPage('https://segmentfault.com/',function(){
      var questions = document.querySelectorAll('.question-stream section');
      var res = [];
      [].forEach.call(questions,function(questionWrap, index){
        var answer = questionWrap.querySelector('.answers');
        var url;
        // 問題dom內,回答數處帶有answered類表示有回答
        if(/\banswered\b/.test(answer.className)){
          url = window.location.protocol 
          +'//'+ window.location.host 
          + questionWrap.querySelector('.summary .title a')
          .getAttribute('href');
          res.push(url);
        }
      });
      return res;
    },function(questions){
      // 這裏用全局變量保存連接,主要是便於後面作遍歷問題訪問頁面
      gQuestions = questions;
      analyzeQuestion();
    });
}

在獲取問題集合以後,thenCbk的下一步就是調用analyzeQuestion

function analyzeQuestion(){
  var url;
  // 該方法主要是遞歸調用,終止條件爲
  //全局變量gQuestions元素個數爲0時終止
  if(gQuestions.length){
    url = gQuestions.pop();
    // 訪問問題頁面詳情
    nightPage(url, function(){
      // 此爲頁面嵌入js,因此訪問不到node做用域下的變量,因而一些方便計算的工具函數,沒法共用
      // 將頁面數據中縮寫爲`1.3k`類的數據作一下轉換(不過好像沒啥做用,主要是瀏覽數那塊會有這類數據)
      function transformNum(str){
        if(/k/.test(str)){
          return parseFloat(str) * 1000;
        }
        return +str;
      }
      
      // 標題、回答數、關注數、收藏數、瀏覽量
      var obj = {};
      
      // #questionTitle a  獲取標題和連接
      var title = document.querySelector('#questionTitle a');
      obj.title = title.textContent;
      obj.url = title.getAttribute('href');

      // .widget-answers article 獲取回答數量
      var answers = document.querySelectorAll('.widget-answers article');
      obj.answer = answers.length;
      
      // .post-topheader__side strong 獲取關注數和瀏覽數
      var topHeader = document.querySelector('.post-topheader__side');

      obj.follow = transformNum(topHeader.querySelector('strong').textContent);
      obj.read = transformNum(topHeader.querySelector('.no-stress').textContent);
      
      // #sideBookmarked 獲取收藏數
      var mark = document.querySelector('#sideBookmarked');
      obj.mark = transformNum(mark.textContent);

      // .post-topheader__side .no-stress
      
      // 返回收集好的數據
      return obj;

    }, function collectDetail(detail){
      //將數據推送到另一個全局變量markLog數組去
      markLog.push(detail);
      console.log(url,'collected');
      // 遞歸調用
      analyzeQuestion();
    });
  }else{
    // 調用完畢,
    end('window ending');
  }
}

寫到這裏,數據的收集也就完畢了,剩下的就是對數據的比對和篩選了。個人篩選就跟我前面描述的同樣簡單的比對

function summaryLog(){
  var allAnswer = 0,
      allFollow = 0,
      allMark = 0;

      fLtA = [];//關注數<回答數+1 提問自動關注
  markLog.forEach(function(log){
    allAnswer += log.answer;
    allFollow += log.follow;
    allMark += log.mark;
    if(log.follow < log.answer + 1){
      fLtA.push(log);
    }
  });

  return '總共問題:' + markLog.length
    + '\n總回答數:' + allAnswer
    + '\n總關注數:' + allFollow
    + '\n總收藏數:' + allMark
    + '\n關鍵數據:個數 ' + fLtA.length
    + '\n詳情:\n'+ simpleFormat(JSON.stringify(fLtA)) + '\n';
}

打印出來的數據畫風大概是這樣的:

clipboard.png

昨天下午寫好的腳本,通過半天的腳本運行間隔,大概的數據都是60左右的問題裏,有9-12條的問題屬於那種,回答數+1>關注數的狀況……彷佛取消回答的被動關注也不是強需求呢……

最後放一張圖 install 一下 B

圖片描述

相關文章
相關標籤/搜索