Model Me, Model World

Model Me, Model World

Blayground

Python 'requests' 保留会话

初步测试

首先测试下怎么样才能登入课程网站,在浏览器里跑一下 Javascript 脚本。 这里加上了一个简单的包头后台才给出了登入成功的回应。

var http = new XMLHttpRequest();
var url = "login.php";
var params = "user=你的学号&passwd=我的密码";
http.open("POST", url, true);

http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

http.onreadystatechange = function () {
  if (http.readyState == 4 && http.status == 200) {
    alert(http.responseText);
    // check if 他的名字 resides in the text
  }
};
http.send(params);

这样测试过之后就放心了,虽然心里早觉得后台会检查包头信息,没想到只检查了一点。

客户端开发

之后选择使用 Python 写一个完整的客户端,Python 一直被用来作为敷衍了事的工具语言。 可惜的是 Python 的某个库的调用函数接口实在太残废,发现一个叫做requests的包看起来很科学,于是乎直接上吧。

# -*- coding: utf-8 -*-
import requests
# headers = {'Content-type': 'application/x-www-form-urlencoded'}
payload = {'user': '爆的学号', 'passwd': '米的密码'}
r = requests.post("http://fm.zju.edu.cn/login.php", data=payload)
r.encoding = 'utf-8'
print(r.text).find(u'花的名字')

也是奇怪,不知道为啥没添加自定义包头也能成功登入(最后一句话打印出来的结果不是-1)(后来才发现这个库调用 post 方法时候会直接加上 Content-type 元数据)。最后一句输出 704,是个好数字。以后大家第一次见面问好的时候可以说:“你在肥老鼠网上的登入后主页的超文本标记语言源码的第 704 个字符后的那几个字是什么呀?”对方回答不上来的话基本就可以判断对方水平和你一样弱爆了。

维持会话

好啦这时候已经能登入了,当然了,思考一下这个课程网站是怎么知道你已经登入了呢,这是一个好问题。检测 IP 吗?看来 masquerade 这个单词可能还比较面生呐。 我们来看下这个request库是不是会自动保存会话信息,真可惜,这次失败了。

r = requests.get("http://fm.zju.edu.cn/showProblem.php?cid=96&pid=1025")
print(r.text).find(u'cmake')

既然如此那大半就是有曲奇吃的节奏了,打开浏览器,输入账号密码。看一下产生了什么曲奇。iPlantDirectoryPro,可能是 OpenAM 了。那么如果想成功获取课程页面里的东西而不被跳转的话那就得把这个曲奇存下来下一次再复制一份喂出去了。

原来是这样想的,其实自己太天真。实际上这个库如果想引入会话概念的话就不能像上面那样每次调用request.get了,应该先建立一个会话,然后在会话里面发功。代码得改成这样,

s = requests.Session()
s.encoding = 'utf-8'

payload = {'user': '不告诉你', 'passwd': '太假了吧'}
r = s.post("http://fm.zju.edu.cn/login.php", data=payload)

r = s.get("http://fm.zju.edu.cn/showProblem.php?cid=96&pid=1025")
print(r.text).find(u'cmake')

这样就能开启一个持续的会话了。

匹配页面内容

鉴权问题解决了之后接下来就十分好办了,使用正则匹配,简单粗暴,行之有效。 首先抓取课程页面里面所有包含有问题的子页面。pid_re = re.compile(r'pid=(?P<pid>\d+)')

在子页面里面获取相应提交数,日期之类的条目。 assignment_re = re.compile(r'Assignment (\d+)')

submit_re = re.compile(
    r'due date:<i>(?P<date>\d+)</i>,max submits:<i>(\d+)</i>,submitted:<i>(\d+)</i>,status: <i>not submitted</i>')

上面两条正则能够直接判断是否有合适的目标被选中。

之后就是纯粹的业务逻辑了,不用多废话解释。

发送邮件提醒

每当检测到页面发生大变时,都应该发送邮件提醒提醒一下自己,或者亲朋好友。发邮件属于比较正常的一个选择,一般手机都设置了邮件客户端,马上就能收到推送。 用到 SMTP 服务,例子网上一搜一大把。

遇到比较有趣的一件事情是如果写成from email import MIMEMultipart就会抛出异常:

'LazyImporter' object is not callable

写成from email.MIMEMultipart import MIMEMultipart,一切顺利,
写成from email.mime.multipart import MIMEMultipart也不会有问题。

不过校网邮箱的没有用标准的 SSL 端口,去邮箱的帮助页面查询下就知道了。

定时检查

谁都不会阻止一个写脚本的去使用crontab,但是这里实在是有点小题大作。 直接写个 bash 脚本每次 sleep 一段时间然后调用一下这个 Python 脚本就行了。

Blog maintained by Tao J

No longer hosted on GitHub Pages — Theme by mattgraham