昨天上知乎一看,发现本身关注的问题接近1000个了,不能忍,但愿控制在500个以之内最好是100个之内。因而打开我关注的问题列表。发现这个列表已经由滚动加载变成了分页,而且不能在问题列表页面直接点取消关注,须要进入问题详情页面去取消关注。这样一来工做量就太大了。
以前滚动加载的时候只要写个小脚本在控制台运行一下就能够把全部的问题加载出来,如今想把全部的问题加载出来就不行了。html
可是做为一个前端,对页面上的东西,老是能够想一想办法的。那就写个小小的chrome插件吧。前端
要实现的功能点:git
思路:github
实现的时候要注意的是何时去点击下一页,在什么时机触发。由于咱们要肯定下一页的数据加载过来了,才能进行下一次点击,否则就可能出现漏页的状况。 观察页面发现每一页的数据加载好,知乎就会把滚动条移动到顶部去。因此咱们能够经过监测scroll事件来判断当前页面的数据是否已经加载完毕。监测到scroll事件的时候就是咱们发起下一次点击的时候。而且当下一页加载好以后咱们要再把滚动条移动到底部去。这样加载新一页的时候滚动条才会再次往上移,从而触发咱们绑定的scroll事件。
另外,就是scroll事件通常会一次性触发好多个。咱们要保证咱们绑定的事件的逻辑代码只执行一次。因此我加了一个timeout定时器,稍微延迟一下。等滚动条停下来的时候才真正执行事件逻辑。在这个timeout运行以前的再次触发的scroll事件都会直接return掉。而且设置一个适当延迟,也减少了被误认为是爬虫的几率。ajax
思路:chrome
为了方便,我就直接写成chrome插件使用了。就不用每次手动到控制台去运行了。
直接拿以前写的一个chrome插件的架子过来开干。
chrome插件的入门写法以及使用我以前有篇文章写过。一个简单的chrome拓展程序开发
而且以前的chrome插件架子里集成了jQuery,代码写起来就更欢快了。api
/* * 功能说明: * 1.把全部关注的问题列出来。 * 2.给全部的问题添加取消关注按钮并完成取消关注。 * * author: liusaint@gmail.com * date: 20180120 */ var ZhiHu = { htmlArr: [], //保存每一页的问题的html数据。 pageItems: {}, //保存每一页的数量。 INTEVAL: 2000, //翻页的时间间隔。请求下一页的间隔。能够调小一些。 timer: '', //定时器 //初始化。 init: function() { var that = this; //绑定滚动事件。当页面滚动了就能够开始请求下一页的数据了。 $(window).on('scroll', this.scrollFn.bind(this)); //初始调用。 this.scrollFn(); //给咱们添加的按钮绑定事件。 $("body").on("click", '.del-q', function(event) { that.delQ($(this)); }); }, //取消关注。拼装url,发送delete请求。 //须要拼装的url接口格式:https://www.zhihu.com/api/v4/questions/20008370/followers delQ: function(jqObj) { var questionUrl, matchArr, delUrl, questionId; //问题页面连接 questionUrl = jqObj.siblings('.QuestionItem-title').find('a').attr('href'); if (!questionUrl) { return; } //正则匹配问题id matchArr = questionUrl.match(/\d+/); if (matchArr) { questionId = matchArr[0]; } delUrl = 'https://www.zhihu.com/api/v4/questions/' + questionId + '/followers'; $.ajax({ url: delUrl, type: 'delete', success: function(data) { //成功的话删除该列。 jqObj.closest('.List-item').remove(); } }) }, //页面滚动时触发的事件。 scrollFn: function(event) { var that = this; //滚动条滚动时会屡次调用此方法,拦截掉。 if (this.timer) { return; } this.timer = setTimeout(function() { //页面内容提取 that.saveData(); //若是有下一页,模拟点击。 if ($(".PaginationButton-next").length > 0) { $(".PaginationButton-next")[0].click(); //移动到底部。 that.scrollBottom(); } else { //到了最后一页了。最后的数据处理。 that.mergeList(); //解绑事件 $(window).off('scroll'); } clearTimeout(that.timer); that.timer = ''; }, this.INTEVAL) }, //从页面中提取问题html数据与每页的数量。 saveData: function() { var html = $(".List-header+div").prop('outerHTML'); this.htmlArr.push(html); //当前页面的问题数量 this.pageItems[$('.PaginationButton--current').text()] = $('.List-item').length; }, //数据收集完成后对列表的处理。 mergeList: function() { var html = this.htmlArr.join(''); //组装全部页的数据到一页。 $(".List-header+div").html(html); //移除分页 $(".Pagination").remove(); //给每一个问题添加取消关注按钮 $(".ContentItem-title").append('<button class="del-q" style="float:right;color:#1388ff;">取消关注</button>'); //把每页的数量打出来看一下,发现并非每页都是20条数据。 top.console.log(this.pageItems); }, //滚动到底部 scrollBottom: function() { var h = $(document).height() - $(window).height(); $(document).scrollTop(h); }, } /* chrome插件部分。核心代码是上面的内容 */ chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { if (request.greeting == "hello") { //执行上面的内容 ZhiHu.init(); } } );
插件完成,加载到chrome浏览器,点击运行。功能正常。大功告成。数组
不过当全部问题都加载出来以后发现了比较奇怪的事情,就是一共加载出来911个问题。而实际上知乎显示我关注的问题有950个。因此我一度怀疑是否是哪一个逻辑有错误少加载了一两页的数据。就在代码里加入了一个对象保存每个问题页面的问题数据。
得出的结果是并非每一页都有20个问题的。有些页面只有19个,最少的甚至只有16个。因而我点开某一页最少的,挨个数一下,发现真是只有16个。而后把这些数据加起来,确实是911个。
另外39个问题真是消失在搜索结果中了。浏览器
本代码具备时效性,仅供参考。知乎的列表的dom结构和接口均可能会修改。若是发现代码不能运行,能够酌情修改代码再运行。app
效果图: