前言,此文章僅作教學用途,如果有人拿去幹別的事情,我概不負責,如果該文章侵害到了掘金社群的利益,請膜法小編立刻聯絡我刪除.
這是我在掘金的第一篇文章,遂想寫個爬蟲教程吧,目標就是掘金,嘿嘿
本文用到的三個工具為
- cheerio:jQuery語法,幫助你在非瀏覽器環境下解析網頁用的
- qs 序列化成url的查詢字串,(不知道說沒說對...)例:{a:1,b2} => a=1&b=2
- request 一個封裝好的好用的請求庫,我自己把它promise化了一下
全部程式碼見github
開始我是嘗試直接請求掘金首頁,然後用cheerio解析,然後拿到網頁繼續幹活的。。可是事情並沒有這麼簡單,通過這個方法爬取的網頁跟我們正常瀏覽的首頁不一樣(有可能是我哪姿勢不對) 沒辦法,只能從介面出發了
首先開啟網頁版掘金, 然後開啟chrome的network,檢視相關請求
咦!recommend?推薦?好了,進去一看,果然是首頁熱門文章,但是。。。 請求引數suid是什麼?檢視請求呼叫堆疊,,再看原始碼,emmmm 原始碼已經被混淆壓縮了這可怎麼辦?我沒有登陸 檢視完所有請求響應都沒看到跟suid有關的,這可咋整?
直接進入請求網址,再更改suid,發現隨便更改都可以得到相應 但是。。。這並沒有什麼用啊!就10條資訊還需要爬嗎?
沒辦法了,只能老套路了。先登陸再說
為了防止頁面跳轉後登陸請求消失,需要先勾選Preserve log,使頁面跳轉後前面的請求不會消失
差點忘打碼了,qwq
模擬登陸
我是使用郵箱註冊的,可能使用其它賬號註冊的介面會不一樣 let data = await request.create({
url: 'https://juejin.im/auth/type/email',
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({email: '155com', password: 'cfdsd.'}) //賬號密碼
});
複製程式碼
直接一個請求搞定,得到如下相應,問題的關鍵就在於cookie
好了,接下來該找介面了,點選最新,發現network多了下面這個請求,其響應資料就是最新板塊的文章
相關引數有來源,裝置id,使用者id,token等,其中最重要的就是token,id什麼的隨便改兩個字元好像也沒問題,但是token錯了它會報illegal token,請求方法錯了,就算引數對了也會報missing src。當你看到token的時候,你會發現,哪都找不到這個資料
但是你仔細看第5張圖的時候,你會發現這auth後面這串字元很像base64編碼
獲取token
開啟相關網站,嘗試解碼
答案呼之欲出啊,最重要的三個引數全在這了,那麼問題來了,node如何解析base64編碼呢?
一行程式碼解決,buffer物件本身提供了base64的解碼功能,最後呼叫toString方法,轉成字串,最後parse得到物件
const cookie = data.headers['set-cookie'];
const encodeToken = cookie[0]
.split(';')[0]
.split('=')[1];
const decodeToken = JSON.parse(new Buffer(encodeToken, 'base64').toString())
複製程式碼
有了token,你就可以隨心所欲的爬了,爬圖片?主題?標題?文章內容?完全o98k
爬圖片
async function getPictureUrl(request, url) {
let data = await request.get({ url });
console.log(data.body.match(/(https:\/\/user-gold-cdn).+?\/ignore-error\/1/g)); //匹配出圖片url,這裡掘金使用了cdn來儲存圖片
}
複製程式碼
爬最新文章
async function getTopics(request, typeKey) {
const { token, clientId, userId } = require('./user.json');
const querystring = qs.stringify({
src: 'web',
uid: userId,
device_id: clientId,
token: token,
limit: 20,
category: 'all',
recomment: 1
});
const types = {
timeline: 'get_entry_by_timeline', //最新
comment: 'get_entry_by_comment', //評論
rank: 'get_entry_by_rank' //熱門
};
const data = await request.get({
url: `https://timeline-merger-ms.juejin.im/v1/${types[typeKey]}?${querystring}`,
headers: {
host: 'timeline-merger-ms.juejin.im',
referer: 'https://juejin.im/timeline?sort=comment'
}
});
const body = data.body;
if (body.s !== 1) { //出錯時清空資訊
fs.writeFileSync('./user.json', JSON.stringify({}));
throw { type: 'token', message: body.m };
} else {
return body.d.entrylist;
}
}
複製程式碼
簡單的處理錯誤
try {
(async function() {
const request = new Riven();
request.setDefaultOptions({
headers: {
Cookie:
'gr_user_id=44868117-2a80-49e8-ba2b-2acd2a77a887; ab={}; _ga=GA1.2.1234597644.150' +
'6904166; MEIQIA_EXTRA_TRACK_ID=0uMtBISQ3EoiMICJMjpaZedfTBz; _gid=GA1.2.100579701' +
'2.1523672771; Hm_lvt_93bbd335a208870aa1f296bcd6842e5e=1521573516,1521573752,1522' +
'270605,1523672771; gr_session_id_89669d96c88aefbc=d54a635e-cece-4f16-aca4-808ae9' +
'2ee559; gr_cs1_d54a635e-cece-4f16-aca4-808ae92ee559=objectId%3A5a974f6ef265da4e8' +
'53d8d52; auth=; auth.sig=25Jg_aucc6SpX1VH8RlCoh6azLU; Hm_lpvt_93bbd335a208870aa1' +
'f296bcd6842e5e=1523675329; QINGCLOUDELB=165e4274d6090771b096025ed82d52a1ab7e48fb' +
'3972913efd95d72fe838c4fb|WtFwy|WtFwr'
}
}); //設不設定cookie都OK的
if (!isLogin()) {
login(request);
}
const topics = await getTopics(request, 'commnet');
for (let i = 0; i < topics.length; ++i) {
//await getDetailData(request, topics[i].objectId);
getPictureUrl(request, topics[i].originalUrl);
await sleep(2000); //偽執行緒掛起
}
})();
process.on('unhandledRejection', error => {
if (error.type === 'token') {
login();
}
console.log(error.message);
});
} catch (error) {
console.log(error.message);
}
複製程式碼
當然,我沒有使用資料庫來儲存資料,這只是教大家爬取原理,到這裡,一個超級簡單的爬蟲就完成了
到最後好像也沒用到cheerio了 ◔ ‸◔?
以上程式碼或言論如有錯誤,還望大家指出
最後,我要吐槽一句編輯器,居然不支援貼上圖片???