在sf里我回答完一个问题,总有一个优先操作,那就是取消关注问题,因为当你回答完一个问题,系统默认你关注了这个问题。这对于有点强迫症的我来说,是一种困扰,因为这样,别人回答了这个问题,系统就会提醒你一次,当一个问题变得热门,很多人来回答的时候,这诸多提醒通知就让人太尴尬了。在sf里,我主张的关注,是自动关注,被动关注啥的,我都不想要。
废话那么一段,于是想着要不写个脚本收集一下问题的关注数跟回答数的关系,猜猜看,这个被动关注是否真的有必要?或者说,我就看看是不是也有人跟我一样回答完也取消了关注问题……
其实这种数据收集,要是有数据库最好了,but,咱又不是sf的员工……
思路
首先在首页获取所有问题(貌似显示80条),但不是所有问题都有回答,于是筛选出有回答数的问题链接
根据上一步获取的链接,一个个的访问问题详情页,收集主要数据(问题标题、链接、收藏数、回答数、关注数、浏览数)
把上一步收集的主要数据做一下记录和筛选(这里筛选,我主要是看看 回答数+1>关注数 的问题,提问者默认关注了问题本身)
实现
用的是nightmare
,需要安装,其基于electron
,安装过程可能需要安装python2.
nightmare
主要是以promise格式来编写的代码,于是我封装了一下一个function
,方便后续编程
// 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();
});
}
于是,页面数据的收集,和后续处理,就取决于evalute
和thenCbk
的逻辑了。
先访问首页,得到问题链接集合:
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';
}
打印出来的数据画风大概是这样的:
昨天下午写好的脚本,经过半天的脚本运行间隔,大概的数据都是60左右的问题里,有9-12条的问题属于那种,回答数+1>关注数的情况……似乎取消回答的被动关注也不是强需求呢……
最后放一张图 install 一下 B