起因
最近手機(jī)用谷歌日歷,發(fā)現(xiàn)可以把課表導(dǎo)入,桌面小插件看課表很方便,然后用js還要開瀏覽器,于是就去了解了下Node.js,發(fā)現(xiàn)可以很方便爬取數(shù)據(jù)
思路
首先獲取網(wǎng)站cookie,然后模擬登陸,再把課表文件下載下來
使用工具
Node.js
superagent, fs 模塊
superagent是一個(gè)輕量級(jí)ajaxAPI,是一個(gè)關(guān)于HTTP方面的一個(gè)庫(kù),使用鏈?zhǔn)綄懛ā?fs是Node.js自帶的模塊,用于與文件系統(tǒng)進(jìn)行交互。
安裝
$ npm install superagent --save
引用
const superagent = require("superagent")
const fs = require("fs")
使用到的代碼
superagent
.[get/post/...](url)
.set()
.send()
.end(function(err,res){
//do something
})
- 設(shè)置請(qǐng)求,參數(shù)中加入請(qǐng)求地址(例如向baidu發(fā)送get請(qǐng)求)
.get("https://www.baidu.com") - 設(shè)置請(qǐng)求頭
- 單個(gè)設(shè)置
.set('Referer','https://www.google.com') - 一起設(shè)置(傳入json數(shù)據(jù))
.set({ "Cache-Control": "max-age=0", "Content-Length": "57", })
- 單個(gè)設(shè)置
- 發(fā)送數(shù)據(jù)(傳入json數(shù)據(jù))
.send({ "sss":"xxx", "kkk":"yyy", }) - 處理返回?cái)?shù)據(jù)
.end(function(err, res){ if(err){ return console("xxx出錯(cuò)" + err) } //對(duì)得到的res做處理 })- res
- res.text包含為被解析的響應(yīng)數(shù)據(jù)
- res.body將解析返回的數(shù)據(jù),但是目前只支持三種格式(application/x-www-form-urlencoded, application/json和multipart/form-data)
- res.header響應(yīng)頭,是一個(gè)Object
- res.type & res.charset 類型和編碼格式
- res.status 狀態(tài)碼
- res
關(guān)于superagent的詳細(xì)內(nèi)容,可以到簡(jiǎn)書這篇文章查看
- fs
fs.writeFile(file, data[, options], callback)是一個(gè)向本地寫入文件的函數(shù)。fs.writeFile("result.json", JSON.stringify(res.body.xkxx), function (err) { if (err) { return console.log("文件寫入失敗" + err) } console.log("文件寫入成功") })- 第一個(gè)參數(shù)是文件名
- 第二個(gè)參數(shù)是寫入的數(shù)據(jù)
- 第三個(gè)參數(shù)是可選參數(shù),指定編碼格式。
- 第四個(gè)參數(shù)是回調(diào)函數(shù),回調(diào)函數(shù)只有一個(gè)參數(shù),就是error
關(guān)于fs模塊的詳細(xì)內(nèi)容,可以到這篇api文檔查看查看
接下來是爬蟲過程
分析頁(yè)面
打開登陸頁(yè)面,清除cookie,F(xiàn)12打開控制臺(tái),切換到Network選項(xiàng)卡,刷新頁(yè)面

發(fā)現(xiàn)4個(gè)文件,很明顯login文件是我們要的,其他都是網(wǎng)站資源文件
點(diǎn)擊login

查看General頭,發(fā)現(xiàn)請(qǐng)求發(fā)放是get
查看response Headers,找到set-Cookie
查看Request Headers,將請(qǐng)求頭復(fù)制下來,以模擬登陸
接下來進(jìn)行第一步,獲得cookie
//獲取cookie, url 和 headers根據(jù)上文在上方定義出來
var cookie
superagent.get(url)
.set(headers)
.end(function (err, res) {
if (err) {
return console.log("獲取cookie發(fā)生錯(cuò)誤")
}
cookie = res.headers["set-cookie"]
console.log("獲得到的cookie為:" + cookie)
//模擬登陸
login()
})
第二步,模擬登陸
勾選上Preserve log(在頁(yè)面刷新或更改之間保留控制臺(tái)歷史記錄。 消息將一直存儲(chǔ),直至清除控制臺(tái)或者關(guān)閉標(biāo)簽。)

然后點(diǎn)擊登錄按鈕

經(jīng)過分析發(fā)現(xiàn)是名為j_spring_security_check的文件接收到Form Data數(shù)據(jù),進(jìn)行登陸驗(yàn)證
點(diǎn)擊文件,查看

發(fā)現(xiàn)請(qǐng)求方法是POST,地址為http://xsjwxt.sxau.edu.cn:7872/j_spring_security_check
繼續(xù)看請(qǐng)求頭,

發(fā)現(xiàn)里面有cookie請(qǐng)求頭,但是我們前面獲取過了,所以將除了cookie的請(qǐng)求頭復(fù)制下來
然后set里面設(shè)置cookie就好啦
接著查看Form Data

多次試驗(yàn)發(fā)現(xiàn)j_captcha1是固定值error(ps: 無語)
然后將Form Data 需要的數(shù)據(jù)寫成一個(gè)json變量,send出去
function login() {
superagent.post("http://xsjwxt.sxau.edu.cn:7872/j_spring_security_check")
.set(headers1)
.set("Cookie", cookie)
.send(sxau)
.end(function (err, res) {
if (err) {
return console.log("模擬登陸出錯(cuò)")
}
//爬取課表頁(yè)面
course()
})
}
然后就進(jìn)入課表頁(yè)面爬取數(shù)據(jù)啦
和上面一樣的操作爬取下來,發(fā)現(xiàn)并沒有得到數(shù)據(jù),經(jīng)過觀察發(fā)現(xiàn)課表是用js代碼在頁(yè)面加載完畢后寫入的,
于是在Network一一查看文件,最后發(fā)現(xiàn)是名為callback的文件返回的是課程數(shù)據(jù)的json格式

于是直接爬取callback文件
經(jīng)過和上面一樣的操作,將課表成功下載到本地
function getClass() {
superagent.get("http://xsjwxt.sxau.edu.cn:7872/student/courseSelect/thisSemesterCurriculum/ajaxStudentSchedule/callback")
.set("DNT",1)
.set("Referer", "xsjwxt.sxau.edu.cn:7872/student/courseSelect/thisSemesterCurriculum/index")
.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36")
//.set("selectionBar",82022)
.set("Host", "xsjwxt.sxau.edu.cn:7872")
.set("X-Requested-With", "XMLHttpRequest")
.set("Accept", "*/*")
.set("Accept-Encoding", "gzip, deflate")
.set("Proxy-Connection", "keep-alive")
.set("Cookie", cookie)
.end(function (err, res) {
if (err) {
return console.log("獲取課表文件失敗" + err)
}
fs.writeFile("result.json", res.text, function (err) {
if (err) {
return console.log("課表文件寫入失敗" + err)
}
console.log("課表文件寫入成功")
})
})
}
查找課程數(shù)據(jù)時(shí),發(fā)現(xiàn)getSectionAndTime文件返回的是課程時(shí)間,于是把getSectionAndTime文件下載下來,然后配合課表文件,就可以很方便處理成Google日歷可以導(dǎo)入的ics文件了。
運(yùn)行結(jié)果:

所有代碼
const superagent = require("superagent")
const fs = require("fs")
//需要的登陸信息
var sxau = {
"j_username": name,
"j_password": password,
"j_captcha1": "error",
}
var url = "http://xsjwxt.sxau.edu.cn:7872"
//請(qǐng)求頭
var headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"Cache-Control": "max-age=0",
"Content-Length": "57",
"Content-Type": "application/x-www-form-urlencoded",
"DNT": "1",
"Host": "xsjwxt.sxau.edu.cn:7872",
"Origin": "http://xsjwxt.sxau.edu.cn:7872",
"Connection": "keep-alive",
"Referer": "http://xsjwxt.sxau.edu.cn:7872/login",
"Upgrade-Insecure-Requests": "1",
},
headers1 = {
"DNT": 1,
"Referer": "xsjwxt.sxau.edu.cn:7872/student/courseSelect/thisSemesterCurriculum/index",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
"Host": "xsjwxt.sxau.edu.cn:7872",
"X-Requested-With": "XMLHttpRequest",
"Accept-Encoding": "gzip, deflate",
"Proxy-Connection": "keep-alive",
}
//獲取cookie
var cookie
superagent.get(url)
.set(headers)
.end(function (err, res) {
if (err) {
return console.log("獲取cookie發(fā)生錯(cuò)誤")
}
cookie = res.headers["set-cookie"]
console.log("獲取到的cookie是:" + cookie)
login()
})
//模擬登陸
function login() {
superagent.post("http://xsjwxt.sxau.edu.cn:7872/j_spring_security_check")
.set(headers)
.set("Cookie", cookie)
.send(sxau)
.end(function (err, res) {
if (err) {
return console.log("模擬登陸出錯(cuò)")
}
getClass()
getTime()
})
}
function getClass() {
superagent.get("http://xsjwxt.sxau.edu.cn:7872/student/courseSelect/thisSemesterCurriculum/ajaxStudentSchedule/callback")
.set(headers1)
.set("Accept", "*/*")
.set("Cookie", cookie)
.end(function (err, res) {
if (err) {
return console.log("獲取課表文件失敗" + err)
}
fs.writeFile("result.json", res.text, function (err) {
if (err) {
return console.log("課表文件寫入失敗" + err)
}
console.log("課表文件寫入成功")
})
})
}
function getTime() {
superagent.post("http://xsjwxt.sxau.edu.cn:7872/ajax/getSectionAndTime")
.set(headers1)
.set("Accept", "application/json, text/javascript, */*; q=0.01")
.set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
.set("Cookie", cookie)
.end(function (err, res) {
if (err) {
return console.log("獲取上課時(shí)間失敗" + err)
}
fs.writeFile("time.json", res.text, function (err) {
if (err) {
return console.log("上課時(shí)間文件寫入失敗")
}
console.log("上課時(shí)間文件寫入成功")
})
})
}