Python3 Requests库爬取课表实战
在读这篇博客之前,请先答应我,不要用 Jupyter Notebook 来写爬虫,它只是用来做数据可视化的一个工具,先不说权限的问题,单单读写文件的编码就烦的一比。
事情的起因是这样的,昨天(5.30)刚刚选完大三第二学期的课,想起来之前同学有在群里发过查询课表的链接,也就是这个xk.urp.seu.edu.cn/jw_service/service/stuCurriculum.action?queryStudentId=\******&queryAcademicYear=**-**-***
打开之后是这个
对应的html源码是这个
按照之前爬虫代码的思路(scrapy还在看文档…),使用Python3 urllib库外加 BeautifulSoup解析response得到目标数据(课表),那些学长也都是这么干的.
但是我们只是想要的是大家课表的.xls文件(BeautifulSoup太烦了,不想写),学校又给了一个下载课表.xls文件的Button,所以试一试能不能从这个Button下手搞一点突破。
首先在浏览器右键检查元素,我们发现这个Button对应了一个JS函数
然后去HTML代码的头部看一下这个函数定义
啊哈,原来导出课表的 Button 对应的是一个runStuExcel.action,那么我们就得到了下载.xls文件对应的链接,即
xk.urp.seu.edu.cn/jw_service/service/runStuExcel.action?queryStudentId=\******&queryAcademicYear=**-**-***
读到这里大家可能会问,为啥不直接点一下Button然后看一下请求呢?小伙伴们可以试一试这种方法,其实也是可以的,而且后者更好
(大家尤其要注意画红线的地方,肥肠重要)
根据方法一得到的下载课表的链接是带参数的(学号及查询年份),但是根据方法二的第二张图得到的Request Url显然没有带参数,那不带参数的url是否正确呢?
很简单,我们新打开一个浏览器,把不带参数的下载课表.xls文件的URL黏贴进去,回车
1 | Request URL: http://xk.urp.seu.edu.cn/jw_service/service/runStuExcel.action |
不出意外报错了
显然,下载链接缺少学号和查询年份这两个必不可缺的参数,所以报错
那么服务器是如何得到参数的呢?我拉上坐旁边的青松学长(他正在一脸开心地看着B站,然后一脸懵逼的被我打断,肥肠感谢~)
“你看这张图,
,Initiator是你查询课表的链接,也就是xk.urp.seu.edu.cn/jw_service/service/stuCurriculum.action?queryStudentId=\******&queryAcademicYear=**-**-***
,而查询课表的链接是带参数的,所以我猜测是你查询的时候给服务器提供了学号及查询年份然后当你下载的时候,服务器根据这些信息返回给你相对应的课表。”
很有道理,让我们来试验一下。
- 输入查询课表A的 url
- 输入下载课表A的 url
- 输入B的下载课表的url
结果下载的两份.xls文件都是A的课表,所以学长说的是对的。
那么问题来了,服务器如何知道我已经发送过查询课表的请求?
Cookie啊!
我们打开浏览器,仔细观察一下查询课表和下载课表这两个请求
我们可以看到两个请求的Cookie是完全相同的,离真相越来越近了
首先,浏览器发送带参数,并且Cookie为None的查询课表请求(第一次查询课表时浏览器没有Cookie),服务器新建Session并返回SessionID保存在本地Cookie里
我们来实验一下
- 清除本地Cookie
- 发送不带Cookie的查询A课表请求
- 服务器响应头,我们可以参考服务器的响应头中包含了Cookie
- 发送查询B课表的请求
请求的Cookie用的是A的Cookie,并且服务器在响应头中并没有设置Cookie,说明Cookie与查询链接的两个参数无关
- 浏览器发送下载B课表的请求,不带参数,带Cookie,正确下载B的课表
至此,真相已经很明显了,我来总结一下
服务器响应并设置的Cookie仅仅是SessionID,浏览器发送带Cookie的查询课表的请求则会更改对应Session的内容(学号,查询日期)。当浏览器发送带有Cookie的下载课表的请求,服务器根据Cookie对应Session中的内容返回相应课表。
至此思路搞清楚了,开始写代码!
- 在浏览器发送带有参数没有Cookie的查询课表的请求,获取Cookie(SessionID)
- 在浏览器发送带有Cookie(SessionID)和参数的查询A课表的请求,更新Session内容
- 在浏览器发送带有Cookie(SessionID)的下载A课表的请求
- 保存响应数据流到本地
- 循环 2~4
完全没有解析的部分,所以代码短了很多,另外学校的网站没有反爬虫机制,出于道德考虑,没有开多线程,也没有设置爬去随机时间间隔
1 | import requests |
爬取到的数据
在写代码中遇到的一些问题…
python编码问题 – 弃用jupyter notebook转向pycharm
保存相应文件到本地 – shutil库