注意!成绩短息通知服务的源码共分为两种:第一种为线性的程序,逻辑简单,但是格式杂乱;第二种为以类作为基础,封装了各种函数,逻辑较为复杂,但是格式清晰。建议新手先学习第一种代码风格,了解代码逻辑后,尝试自己完成第二种开发
上节课,我们成功的登录了某方的教务系统,同时已经得到了我们想要获取的成绩页面。这节课,我们将首先提取成绩页面中的有效信息,然后将相关信息保存至数据库中,最后,使用短信接口发送成绩通知。相关的代码,你仍然可以在本课程对应的Github页面获取,欢迎各位同学在讨论区进行讨论!
首先让我们分析一下成绩页面的HTML格式:
<table class="datelist" cellspacing="0" cellpadding="3" border="0" id="Datagrid1" width="100%">
<tr class="datelisthead">
<td>学年</td><td>学期</td><td>课程代码</td><td>课程名称</td><td>课程性质</td><td>课程归属</td><td>学分</td><td>绩点</td><td>成绩</td><td>辅修标记</td><td>补考成绩</td><td>重修成绩</td><td>学院名称</td><td>备注</td><td>重修标记</td>
</tr><tr>
<td>2016-2017</td><td>2</td><td>1310040</td><td>ASP.NET应用开发1</td><td>专业选修课</td><td> </td><td>3.5</td><td>1.9</td><td>69</td><td>0</td><td> </td><td> </td><td>计算机与软件工程学院</td><td> </td><td>0</td>
</tr><tr>
<td>2016-2017</td><td>2</td><td>2011536</td><td>形势与政策Ⅵ</td><td>通识必修课</td><td> </td><td>0.0</td><td>3.5</td><td>良好</td><td>0</td><td> </td><td> </td><td>马克思主义学院</td><td> </td><td>0</td>
</tr>
</table>
从上面的HTML源码中,我们可以清晰的看到,所有的信息都存在于id为Datagrid1
的表格中。因此,我们首先要提取Datagrid1
表格中的所有tr
:
soup = BeautifulSoup(score_text.content, "lxml",from_encoding="gb18030")
trs = soup.find(id="Datagrid1").findAll("tr")
上述代码和我们之前遇到的用法不太一样,因此我们要详细说明一下上述代码中的相应参数:
soup = BeautifulSoup(score_text.content, "lxml",from_encoding="gb18030")
首先是score_text.content
,在以前的开发中,我们使用的是text
方式获取网页信息,这里我们使用了’content’方式的原因是,某方返回的数据是gb2312
格式的内容,因此我们需要直接用二进制的方式来传递内容,同时,我们也通过from_encoding
来进一步的向BeautifulSoup说明我们的原始参数。
接下来我们要解析tr
中的所有数据:
Grades = []
for tr in trs:
tds = tr.findAll("td")
tds = tds[:2] + tds[3:5] + tds[6:9]
oneGradeKeys = ["year", "term", "name", "type", "credit","gradePonit","grade"]
oneGradeValues = []
for td in tds:
oneGradeValues.append(td.string)
oneGrade = dict((key, value) for key, value in zip(oneGradeKeys, oneGradeValues))
Grades.append(oneGrade)
上述代码中,我们遍历了所有tr
中的td
内容。需要指出的是,我们获取成绩,仅仅需要成绩所对应的年份
,学期
,课程名
,课程性质
,学分
,绩点
,成绩
。因此在获取到td
中的数据库后,我们需要剔除一些无用的数据。具体的,原始数据为:
[<td>2016-2017</td>, <td>2</td>, <td>1310040</td>, <td>ASP.NET应用开发1</td>, <td>专业选修课</td>, <td> </td>, <td>3.5</td>, <td>3.7</td>, <td>87</td>, <td>0</td>, <td> </td>, <td> </td>, <td>计算机与软件工程学院</td>, <td> </td>, <td>0</td>]
整理后的数据为:
[<td>2016-2017</td>, <td>2</td>, <td>ASP.NET应用开发1</td>, <td>专业选修课</td>, <td>3.5</td>, <td>3.7</td>, <td>87</td>]
接下来,让我们打印一下我们的成果
注意:因为传递的内容编码为
gb2312
格式,因此我们需要定义python程序的默认格式
# coding=gbk
for score in Grades:
year = score["year"]
term = score["term"]
gradepoint = score["gradePonit"]
grade = score['grade']
print(gradepoint,end='\t')
print(grade,end='\t')
name = score["name"]
print(name)
Bingo!我们已经获取到了相关的成绩信息!接下来让我们将数据存入数据库中
因为数据库的操作,我们在之前的章节中就已经学习过,因此本文直接给出对应的代码。
CREATE TABLE SCORE(
id INT AUTO_INCREMENT PRIMARY KEY,
year_ varchar(12),
term_ VARCHAR(12),
name_ VARCHAR(12),
type_ VARCHAR(10),
credit_ VARCHAR(10),
gradePoint_ VARCHAR(10),
grade_ VARCHAR(10)
)ENGINE=INNODB;
import pymysql
db = pymysql.connect("localhost","root","654321","mysql" )
cursor = db.cursor()
def insert_info(year, term, name, type, credit,gradePonit,grade):
sql = "INSERT INTO score(year_, term_, name_, type_, credit_,gradePoint_,grade_) VALUES('"+str(year)+"','"+str(term)+"','"+str(name)+"','"+str(type)+"','"+str(credit)+"','"+str(gradePonit)+"','"+str(grade)+"')"
print(sql)
cursor.execute(sql)
db.commit()
####判别是否发送短信
3. 从数据库中判断
为了精确的发送短信,我们在获取到信息之后,从数据库中查询是否存在对应的课程名称,如果存在,则不发送;如果数据库中不存在该门课程的信息,则发送短息:
int result = select_info(name)
if result!=0:
time.sleep(60*60)
else:
send_mail(year,term,name,type,credit,gradePoint,grade)
juage=[]
if name not in juage:
send_mail(year, term, name, type, credit, gradePoint, grade)
juage.append(name)
else:
print("已经存在,不发送短信")
####发送短信
发送短息我们已经学习了很多的方法。因此本文并不指定大家选择哪个方法。课题提供的代码中,使用的是通过139信箱发送短信,大家可以使用其他的方法进行操作。
####系统说明
5. 为了保证该系统全天为我们服务,我们可以将主函数一直运行,每次运行后使用time.sleep()
函数,使服务休眠1小时或者你指定的时间;
6. 因为验证码目前需要手动输入,因此建议使用Windows服务器配置环境。(后续将会提供自动识别验证码服务,大家以后可以迁移至Linux服务中)
在运行之前,请确你已经配置好下面的参数:
用户名
用户密码
教务网地址
教务网验证码地址
查询成绩地址
数据库连接相关信息
邮件发送的相关信息
配置好上述参数的信息后,你的手机肯定会收到这样一条短信:
没错!你已经完成了成绩通短信通知服务!那么,为什么不参考之前文章中的知识,让它在云服务器中为你服务呢?