第一次用 JavaScript (node.js) 来写后端,基本上也是因为吃了 @ntzyz 的传教才做了这样的选择。虽然完全没有技术含量但当做是对新玩物的 HelloWorld 心态,也还是纪念一下(
需求的起始是从一个舍友突然和我讲想知道图书馆什么时候人最少什么时候人最多开始的。因为在图书馆的预约网站可以轻易查询到剩余座位的人数,那我只要把这些数据收集保存下来,然后由前端来渲染成图表就可以实现了。很简单。
后端实现
首先,我们观察查询页面的 HTML DOM。
发现所需要的数据就在页面的 div.col-xs-12.col-md-4
的 span 下;
所以不需要操心别的事情,只需要对 DOM 来解析就好了。
那么,现在的任务就是:
1、构造一个对本站 HTML 文件的请求
2、对 HTML 中的 DOM 进行解析并取出所需要的数字
针对上面的需求:
我选择使用 axios 来完成对 HTML 的 get 请求,它极为流行使用简单;
再用 cheerio 来完成 DOM 元素的解析,几乎完全兼容 jQuery 很方便。
async function fetchNumFromHtml() { let counterDomNum = 0 await axios.get("http://example").then(function(response) { const $ = cheerio.load(response.data, { decodeEntities: false }) const counterDom = $("div.col-xs-12.col-md-4 > span").text().slice(0, -1) counterDomNum = parseInt(counterDom) }). catch (function(error) { console.error(error) }) return counterDomNum }
使用 decodeEntities: false
来禁止汉字受到多余的编码。这样取出整个 span 里的文本后,.slice(0, -1) 截掉后面的“人”字后,进行强制类型转换。
这样,就可以拿到当前实时的剩余座位的数量了。
可以考虑数据以什么样的形式进行展现了。我打算每五分钟记录一次当前人数,然后将一天的趋势以 API 的形式暴露给前端以绘制成折线统计图,共展示七天的数据。
这样,现在要在后端实现的功能就是:
1、每五分钟记录当前的数据,并定期将数据存储至本地文件以持久化保存。
2、实现一个 web 服务器,用来发送七天内的数据 json
fs.writeFile
和 setInterval
可以完成第一项任务。为了取得可以对齐时间轴的更规整的数据又不要数据量太多,我选择采用“整五分钟”时的数据;如果每五分钟收集一次数据的话,每天需要收集288次数据。也就是准备一个 arr.length = 288 的数组就好了。
我们要保存一共七天的数据,所以实际上需要的是一个二维数组。在 JavaScript 中,并没有提供二维数组的功能,不过只要实现一个结构形如 arr[[]] 的数组倒也可以充当这样的二维数组功能来使用,这部分可以这样写:
// 实现一个 7*288 的数组代表七天的 288 个数据 // 如果想要访问第二天第 144 个数据,只要访问 arrData[2][144] 就可以了 let arrData = new Array(7) for (let i = 0; i < arrData.length; i++) { arrData[i] = new Array(288) } // 半分钟一次触发,检查时间是否整五分钟 setInterval(function() { const myDate = new Date() // 整五分钟时将数据存入对应时间数组下标 if(myDate.getMinutes()%5 === 0) { savaDataToArr() } },1000*30) // 向指定日期下标的数组中存储数据 function savaDataToArr () { const myDate = new Date() const subscript = myDate.getHours()*12 + Math.floor(myDate.getMinutes()/5) const day = myDate.getDay() fetchNumFromHtml().then(num => { arrData[day][subscript] = num console.log(num + ' in ' + subscript + ' at ' + day) }) }
保存数据部分的代码平平常常没有什么好说的。至此,所有需要的数据都准备好了,只待将 arrData 发送!
因为第一次摸,缺乏经验,这里走了弯路,用了不是很推荐的 http 来实现这样的功能。但好在项目的复杂程度并不高,这样写又不是不能用,就没有做其他的修改了。
http.createServer(function(request, response) { response.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }) response.end(JSON.stringify(arrData)) }).listen(port)
此时,我们就可以通过浏览器访问到收集的数据了!后端部分完成。
前端实现
当然是使用现有的图表库了,我选用了 demo 配色也可可爱爱的 Chart.js 来完成。
具体实现没有什么好说的,但他们网站的文档写的可真烂,我最终居然在 csdn 才找到一遍系统有参考价值的使用说明。然后参照他们官方的 demo 源码,抄出了一个最终能看的图表出来。
值得一提的是,这个库所需要的 x轴 标签居然要严格对应数据,所以我一天 288 个点就需要288个标签,这太不科学美观了,所以这里用以下方法只生成整小时的 x轴 标签。
function genLabels() { let j = 0 for (let i = 0; i < arrData[0].length; i++) { if (i % 12 === 0) { labels[i] = j.toString() + ':00' j++ } else { labels[i] = '' } } }
最后我的屑代码也可以在我的玩具仓库找到,如果想找点实例来参考的话。
数据解读(不
每天五点钟服务器开机,在六点开馆之前都不会有任何人进入预约。不过有趣的是,似乎每天开放预约的人数都不太一样?虽然说疫情期间图书馆的管理确实很严格也有很多地方没有开放,但每天都差那么一点点也不知道是为什么。
从很早的时候,大家一股脑就都钻进图书馆抢座位。这之后一部分学生选择先去吃饭,也有坚持在这里读书一上午的人,吃过饭的同学们也会很快回到图书馆重新就坐。大家对吃饭这件事都是很积极的,毕竟食堂的座位也要抢在高峰期才有更好的就餐体验。
周六日会有更多的同学会选择去玩,相应地,也会有更多的座位空下来,所以周六日绝对是非常适合在图书馆度过整日的选择。
后续更新
换用了更靠谱的接口,这次直接展示当前人数,也顺便把它部署到服务器上去运行了,可以在 http://shellbin.me/lib/counter 看到运行效果