电光学院通知爬虫 for RSSHub

为 RSSHub 增加了 NJUST 电光学院的爬虫支持,支持电光本科各年级级网的通知和每日动态的抓取,配合 RSS 客户端可以实现级网更新的实时通知。

于是,现在我也算是 15k stars 大型开源项目Contributor 了呢 😎

pull-requests

顺便把因为级网系统升级已经失效的研究生院爬虫也给重新实现了一下。当然,由于各个学院的通知网站在结构上大差不差,理论上只需要修改相应的网址即可适用于 NJUST 的任何一个学院。

RSS 链接规则如下:

url

比如电光 17 年级通知的 RSS 订阅地址:https://rsshub.app/njust/eo/17/tz

更新: 非常不幸 😢,在本文写完后不久,RSSHub 的官方演示服务器 rsshub.app 就因为不可抗力无法从国内访问了。好在 RSSHub 是开源的,可以利用 VPS、NAS 或树莓派等设备自建,也可以寻找网络上其他人公开提供的服务,比如 IOIOX 提供的 RSS Forever 服务

另外,由于 RSSHub 官方演示服务器(以及大部分 RSSHub 公益服务器)的缓存刷新时间限制,其实并不能做到“实时”(至少有 15 分钟的延迟),需要更快的更新速度可以考虑自建。或者,17 级的同学也可以直接使用我自己维护的这个订阅源:

https://rpi.jasongzy.com/dg17.xml

RSSHub 上部署的爬虫基于的是 Node.js 和 puppeteer(级网升级后必须使用无头浏览器才能抓取到有效信息),因为性能问题仅仅抓取了通知的标题、链接和发布日期(尝试过抓取每篇通知的正文内容,但同步请求各个地址太慢,异步又会导致 CPU 和内存爆满);而上面我自己部署的 RSS 源则是用 Python(selenium、chromedriver)生成的,代码逻辑类似,但增加了正文内容的全文输出(标准 html 格式),配合 RSS 客户端可以完美地显示带格式的正文、图片甚至附件,而不必再跳转到原网页查看。

demo

如果有小伙伴(大概率是学弟学妹吧)有更高效的实现方式,比如直接找到请求接口而不用无头浏览器,欢迎修改相关的代码并提交 PR 哦~


以下是事情的原委

很久以前,级网就因为它极其糟糕的移动端体验(尤其是手机上的大图查看)诱发了我想要给它“换个用户界面”的打算。

记得我大二的时候,级网还是静态的 html 页面,当时用 curl + sed 写了个 shell 脚本就可以抓通知链接地址:

1
curl http://dgxg.njust.edu.cn/_s99/_t689/njtz/list.psp | grep "page.htm" | grep -o "/_t689.*htm" | sed 's/^/http:\/\/dgxg.njust.edu.cn\/_s99&/g' >test

之后我还先后用过 DistillFeed43Huginn 以及 Android 手机上的 Web Alert 等等手段来对级网进行监控,力求能够做到实时获取通知消息。

后来不知从哪一天开始,级网它居然更新了反爬机制!(真的有很多人像我这么无聊去爬级网的吗……)大概我院知名的「电光 18 小程序」下线年级网站功能也是因为这个原因吧……

dg18

反爬机制具体来说就是异步加载+特征检测。这一变化导致上述的那些网页抓取工具几乎都失效了,只剩 Distill 和 Web Alert App 勉强还能用。Distill 作为 Chrome 扩展可以直接渲染网页,Web Alert 则支持运行网页的 js 脚本,因此这两者仍然可以正常工作。然而前者依赖浏览器,无法做到 24 小时后台监控;后者依赖手机,如果忽略耗电等等问题的话,倒是可以锁住后台作为一种有效的解决方案,并且如果购买了增强包的话,还支持与 Tasker 的联动,理论上就可以触发包括推送服务在内的各种各样的事件。

当然,即使是仅存的这两个工具也只能做到在网站有变动时进行通知,具体的内容还是得自己去网站上看,未免有些事与愿违。于是我继续研究爬虫。现在如果用 Python requests 去访问级网,返回的未经渲染的内容是宛若乱码的 js 函数;而用无头浏览器(selenium/puppeteer 结果都一样)去访问则会直接 400 Bad Request…🌚

后来查阅了大量关于反反爬的资料,修改了请求中的好几个参数,终于发现关键点是 window.navigator.webdriver 这个参数,如果其值为 true,将会返回 400,只有当其为 undefined 时才能正常渲染出内容。在未闻 Code 这篇文章的提示下,终于成功通过了级网服务器的考验,顺利拿到网页内容。之后的元素定位和内容获取用 BeautifulSoup 或者 xpath 都很容易实现。

至于 RSS 的格式化输出,RSSHub 上的版本当然直接用的是它提供的轮子,Python 版本用的则是 PyRSS2Gen 这个库。

👉 Python 版的完整代码见 Gist

目前这个爬虫每天会定时抓取,其他时间一旦级网有更新,通过前面提到过的在我手机上持续运行的 Web Alert 也能够及时发现,并与 Tasker 联动,通过 Webhook 自动触发服务器上的爬虫更新,总之基本可以做到与级网实时同步更新。

最后,两版爬虫代码的编写感受:有一说一,Node.js 的单线程高并发是很 🐮🍺 的,但是从 C/C++ 入门的非 CS 专业学生表示,异步回调的逻辑写起来让我很痛苦。再回头看 Python,真香!

更新于 2022-05-03:毕业之后,我发现 NJUST 的大部分网站(包括级网在内)再次更新了反爬策略,使得上述的反反爬方法失效了。这次我改用了号称包含“all tricks to hide puppeteer usage”的 puppeteer-extra-plugin-stealth介绍),再次向 RSSHub 提交了代码,修复了 NJUST 所有失效的路由,顺便增加了“电光学院官网”和“电光学院研学网”两个新路由。另外,对级网的支持也不再局限于 16-19 级,其他年级只需要替换参数为相应的 URL Path 即可,详见 RSSHub 官方文档

-------------本文结束    感谢您的阅读-------------
0%