用 NodeJS 爬取知乎的關系鏈
一、介紹
NodeJS單線程、事件驅動的特性可以在單臺機器上實現極大的吞吐量,非常適合寫網絡爬蟲這種資源密集型的程序。
這段時間寫了一個可以爬取知乎關系鏈的小爬蟲,輸入某個用戶的用戶主頁URL,就可以爬取他的關系鏈:
二、爬蟲的實現
數據請求方面使用了 request 這個模塊,用express響應請求,前端構圖使用了 Echarts ,中間數據交互是用websocket做的。
主要用到了知乎的兩個API:
//獲取目標用戶的關注者
POST https://www.zhihu.com/node/ProfileFollowersListV2
參數:
method:"next", //填入next即可
params:{
offset:40, //20的倍數,從0開始每次拉取20個關注者
order_by:"created", //填入"created"即可
hash_id:"d965f32a168564f9e58ad3a48a1585a4" //目標用戶在知乎唯一的hash_id
},
_xsrf:"289c6ef5534d3dbb6a54057826864799" //xsrf參數,cookie中給定的
//獲取目標用戶關注的人
POST https://www.zhihu.com/node/ProfileFolloweesListV2
參數:
method:"next", //填入next即可
params:{
offset:40, //20的倍數,從0開始每次拉取20個關注的人
order_by:"created", //填入"created"即可
hash_id:"d965f32a168564f9e58ad3a48a1585a4" //目標用戶在知乎唯一的hash_id
},
_xsrf:"289c6ef5534d3dbb6a54057826864799" //xsrf參數,cookie中給定的
爬蟲的工作流程如下:
1. 獲取目標用戶的關注者、關注的人列表,找出和他相互關注的人(即朋友)
2. 對朋友列表里的朋友重復1中的步驟,找出朋友的朋友列表
3. 遍歷2中的結果,找出朋友之間的相互關注關系
三、部分代碼
首先我們寫一個 getUser 方法,它的作用是請求一個用戶主頁URL,獲取請求結果,解析出用戶的昵稱、hash_id、關注者數量、關注的人數量。
var request = require('request');
var Promise = require('bluebird');
var config = require('../config');
function getUser(userPageUrl) {
return new Promise(function(resolve, reject) {
request({
method: 'GET',
url: userPageUrl,
headers: {
'cookie': config.cookie
}
}, function(err, res, body) {
if (err) {
reject(err);
} else {
resolve(parse(body));
}
})
});
}
function parse(html) {
var user = {};
var reg1 = /data-name=\"current_people\">\[.*\"(\S*)\"\]<\/script>/g;
reg1.exec(html);
user.hash_id = RegExp.$1;
var reg2 = /關注了<\/span><br \/>\n<strong>(\d*)/g;
reg2.exec(html);
user.followeeAmount = parseInt(RegExp.$1);
var reg3 = /關注者<\/span><br \/>\n<strong>(\d*)/g;
reg3.exec(html);
user.followerAmount = parseInt(RegExp.$1);
var reg4 = /<title> (.*) - 知乎<\/title>/g
reg4.exec(html);
user.name = RegExp.$1;
return user;
}
module.exports = getUser;
接下來需要一個 fetchFollwerOrFollwee 方法,它的作用是輸入上面的user對象,根據在第二部分介紹的API,抓取出用戶的所有關注的人或者關注者,使用方法類似(使用es6):
getUser('someURL')
.then(user => fetchFollwerOrFollwee({user: user, isFollowees: false})
.then(list => console.log(list))
具體代碼參照這里,就不貼上來了
接下來要做的就是組合 getUser 和 fetchFollwerOrFollwee ,變成一個 getFriends 方法,輸入是用戶頁URL,輸出是用戶的好友列表,大概像這樣:
function getFriends(someURL){
getUser(someURL)
.then(user => fetchFollwerOrFollwee(...))
.then((followersList, follweesList) => findFriends(followersList, follweesList))
}
然后我們可以封裝一個最后的 searchSameFriend 方法,輸入是某個user和一個好友列表myFriends,輸出是這個user的所有好友中,也在列表myFriends中的好友
function searchSameFriend(user, myFriends){
return getFriends(user.url)
.then(user => findSameFriends(userFriends, myFriends))
.then(sameFriends => console.log(sameFriends))
}
最后整個爬蟲的promise流程大概是這樣的:
function Spider(){
return getUser(URL)
.then(user => getFriends(user))
.then(userFriends =>
Promise.map(userFriends, friend => searchSameFriend(friend,userFriends))
)
}
當然其中缺少了部分用websocket和前端數據交互的代碼
來自:http://zhuanlan.zhihu.com/p/20683518