起因
昨天上知乎一看,發現自己關注的問題接近1000個了,不能忍,希望控制在500個以以內最好是100個以內。於是開啟我關注的問題列表。發現這個列表已經由滾動載入變成了分頁,並且不能在問題列表頁面直接點取消關注,需要進入問題詳情頁面去取消關注。這樣一來工作量就太大了。 之前滾動載入的時候只要寫個小指令碼在控制檯執行一下就可以把所有的問題載入出來,現在想把所有的問題載入出來就不行了。
但是作為一個前端,對頁面上的東西,總是可以想想辦法的。那就寫個小小的chrome外掛吧。
要實現的功能點:
- 一次性把所有的問題載入出來。
- 就在問題列表頁面取消關注。
一次性載入所有問題
思路:
- 從第一頁開始,依次模擬點下一頁的按鈕。每次點之前把當前頁的問題列表的資料儲存成html字串。放進一個陣列。
- 沒有下一頁按鈕的時候,表示已經到了最後一頁。拼裝所有的html字串。替換最後一頁的列表區。
實現的時候要注意的是什麼時候去點選下一頁,在什麼時機觸發。因為我們要確定下一頁的資料載入過來了,才能進行下一次點選,不然就可能出現漏頁的情況。 觀察頁面發現每一頁的資料載入好,知乎就會把滾動條移動到頂部去。所以我們可以通過監測scroll事件來判斷當前頁面的資料是否已經載入完畢。監測到scroll事件的時候就是我們發起下一次點選的時候。並且當下一頁載入好之後我們要再把滾動條移動到底部去。這樣載入新一頁的時候滾動條才會再次往上移,從而觸發我們繫結的scroll事件。 另外,就是scroll事件一般會一次性觸發好多個。我們要保證我們繫結的事件的邏輯程式碼只執行一次。所以我加了一個timeout定時器,稍微延遲一下。等滾動條停下來的時候才真正執行事件邏輯。在這個timeout執行之前的再次觸發的scroll事件都會直接return掉。並且設定一個適當延遲,也減小了被誤認為是爬蟲的概率。
就在問題列表頁面取消關注
思路:
- 給每個問題加按鈕。直接append就行了。並繫結事件。
- 從問題的dom結構中拿到問題的url,並從url中解析出問題id。
- 到問題詳情頁分析取消關注的url格式,使用問題id拼裝。
- 自己發ajax請求。delete格式。
實現
為了方便,我就直接寫成chrome外掛使用了。就不用每次手動到控制檯去執行了。 直接拿之前寫的一個chrome外掛的架子過來開幹。 chrome外掛的入門寫法以及使用我之前有篇文章寫過。一個簡單的chrome擴充程式開發 並且之前的chrome外掛架子裡整合了jQuery,程式碼寫起來就更歡快了。
/*
* 功能說明:
* 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結構和介面都可能會修改。如果發現程式碼不能執行,可以酌情修改程式碼再執行。
效果圖: