Gotit
gotit开始时叫做正方教务系统查询工具, 仅用来帮助校外同学们查询本学期的成绩. gotit的名字只来源于我偶然买到的这个域名gotit.aisa, 当初.asia域名首年十八元促销, 就买到了这个. Gotit维护至今, 一直没有好好总结一下, 期间停停落落, 真的体会到了做产品的艰难, 出状况的时间不是自己说了算的, 使用人数多的时候每一秒都有十几个人同时访问, 每一个小小的失误, 都会给很多用户造成极大的困扰.
2013-01-09至2013-07-31
最初方法
使用urllib2
模拟登录,然后使用BeaufitualSoup
解析出所需的表格内容. 登录难点是url
中有一段随机字符串,需要先将其匹配出来获得base_url
, 一开始时是每一次登录都匹配一次该随机字符串,也就是说查询一次成绩程序要访问两次正方教务系统, 现在使用了马伟伟同学写的cache
模块,每500秒获取一次该字符串,提高了成绩查询速度.另一个就是模拟登录时的post
内容中有一个VIEWSTATE
值, 类似与csrf_token
吧, 登录时需要先抓取该值然后post
.
test
部署相关
- web.py
话说第一次上线比较急, 是直接通过web.py直接跑的, 不过也没怎么有压力, 估计是当初PV太低了
- apache + mod_wsgi
这才算是第一次部署吧, 通过apache和mod_wsgi跑的web.py, 不过由于服务器上没有公网IP, 所以外面又加了一个nginx反向代理
详见: http://zhwei.42qu.com/14334743#h21
- apache + gunicorn
话说我是不太喜欢用apache的, 因为每次重启服务时还需要重启下apache, 太蛋疼了, 不过机器上的nginx是当初lnmp一键安装包里的, 很难用, 只能用自己装的apache了, gunicorn更pythoner点, 另外支持多线程.
详见http://zhwei.42qu.com/14840780
- nginx + gunicorn
现在用的, 换了一台服务器, 配置好些, 原来的做备份用了.
中文验证码
正方教务系统从原来的数字验证码到现在的中文验证码,尝试过验证码识别, 但是都不怎么理想,所以选择了让用户自己识别验证码,我们只模拟登陆。这样需要满足几个条件。
首先,成功获取验证码并保存为图片,其中图片命名遇到了问题,起初选择了使用随机整型数字命名,由于程序中的随机都是伪随机, 重合率很高!后来选择了使用时间的MD5值,一直沿用到现在作为用户的唯一识别码,使time.time()
获得的时间精确到0.001秒,以现在的用户访问量遇到在同一0.001秒同时访问几乎是不可能的,并且事实也是如此。
用户需要在页面上直接输入学号、密码和验证码, 这就需要我们在用户第一次访问时就为其抓取下验证码供其识别,用户填写后由后台使用用户的数据进行模拟登陆从而抓取下成绩内容或者其他信息。而此处的难点是:怎样保证用户识别的验证码和他应该提交的验证码是同一个。因为正方系统还为每一个页面状态提供了一个VIEWSTATE
参数,每次post都需要提供该参数。我一开始的想法是能够解析出每次抓取时的COOKIE不过后来想通了,由于每次都是在服务端抓取, 每次的COOKIE都是一样的,而每次的VIEWSTATE却是不一样的,所以这个方法被否定了。
ma6174同学想出了另一个方法,就是:创建一个独立于WEB.PY
之外的字典,通过键值对的方式,将每一次处理时的对象直接保存起来,此处对象中有两个关键方法,一个是pre_login
另一个是login
,前者用于抓取验证码、viewstate,并且将此处的对象内容以某一KEY保存在字典中,该KEY开始时直接使用的VIEWSTATE,不过在本学期末发现: VIEWSTATE在一段时间内不变了, 这就导致了字典中的某一值一直被重写,不过这种情况在本地测试是正常的,因为只有一个人访问。为了避免这个问题就再次使用了上面说的time_md5
。
现在的做法就是:以time_md5
为键,将由VIEWSTATE和对象组成的元组保存在全局字典中,验证码图片也是直接以time_md5
命名,由于http的无状态,我们也懒得再弄一个cookie,所以直接将time_md5
传到页面作为一个hidden input,之后再从post值中取出使用。
URL中的随机字符串
相对来说,这个问题算很小的了,不过已有一些小纠结。第一次由于132的链接登陆后内容异常,只能使用133,而133的链接就多了这里的主角随机字符串
, 当然这里随机是对我们来说的,正方教务系统后台肯定会有所控制,该字符串在一段时间内是不会发生改变的,我也只等了五分钟,对用户来说五分钟已经足够完成正常的查询功能了。
解决方案: 每次抓取前先获取一下重定向后的链接, 匹配出随机字符串, 因为之后所有访问的url都需要此字符串, 这里也要保证登陆的url和登陆成功后抓取的url中的字符串是一致的, 不然肯定抓不到页面.
之后有了中文验证码后, 再次使用133连接时(平时132能正常使用), 按以前的方法却出现了问题, 每次抓取前获取随即字符串在真正模拟登陆时却总是不行, 随后把获取重定向url的方法直接放在对象(保存到字典中的对象)中后就没问题了.
APIs
提供api… 挺蛋疼的一件事…现在提供的json格式的api, 文档见这里, 现在主要是完成了正方教务系统里的成绩查询, 考试时间查询和课表查询的格式还没有处理好.