chapter12-实战成绩通知短信系统(下)

news2024/12/28 9:59:15

注意!成绩短息通知服务的源码共分为两种:第一种为线性的程序,逻辑简单,但是格式杂乱;第二种为以类作为基础,封装了各种函数,逻辑较为复杂,但是格式清晰。建议新手先学习第一种代码风格,了解代码逻辑后,尝试自己完成第二种开发
上节课,我们成功的登录了某方的教务系统,同时已经得到了我们想要获取的成绩页面。这节课,我们将首先提取成绩页面中的有效信息,然后将相关信息保存至数据库中,最后,使用短信接口发送成绩通知。相关的代码,你仍然可以在本课程对应的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!我们已经获取到了相关的成绩信息!接下来让我们将数据存入数据库中

保存数据到数据库

因为数据库的操作,我们在之前的章节中就已经学习过,因此本文直接给出对应的代码。

  1. 创建数据表
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;
  1. 在Python操作数据库
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)
  1. 从数组中判断
    从数据库中获取信息,经过数据库连接、数据库关闭等操作,不仅浪费了时间,而且还消耗了大量的系统资源。因此,我们可以定义一个全局变量,将相关信息保存在变量中。判断新的课程是否存在变量中:
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服务中)

系统测试

在运行之前,请确你已经配置好下面的参数:

  • 用户名

  • 用户密码

  • 教务网地址

  • 教务网验证码地址

  • 查询成绩地址

  • 数据库连接相关信息

  • 邮件发送的相关信息
    配置好上述参数的信息后,你的手机肯定会收到这样一条短信:
    在这里插入图片描述

    没错!你已经完成了成绩通短信通知服务!那么,为什么不参考之前文章中的知识,让它在云服务器中为你服务呢?

    总结
  1. 本文存在着大量的编码转化、数据存储操作。希望大家有问题一定要去讨论区进行讨论,我会第一时间为大家解答;
  2. 还是要提醒大家:项目需要配置的信息很多,希望同学们能够静下心来,耐心的跟着文章学习;
  3. 自动识别验证码目前的准确率已经达到了90%,我将会尽快更新相关代码。可能你看到这篇文章的时候,就不需要人工识别验证码了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1402417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2. SpringBoot3 实战之用户模块接口开发

文章目录 开发模式和环境搭建开发模式环境搭建 1. 用户注册1.1 注册接口基本代码编写1.2 注册接口参数校验 2. 用户登录2.1 登录接口基本代码编写2.2 登录认证2.2.1 登录认证引入2.2.2 JWT 简介2.2.3 登录功能集成 JWT2.2.4 拦截器 3. 获取用户详细信息3.1 获取用户详细信息基本…

优化算法--李沐

目录 1.1梯度下降 1.2随机梯度下降 1.3小批量随机梯度下降 1.4冲量法 1.5 Adam 损失值也就是预测值与真实值之间的差值是f(x)&#xff0c;x是所有超参数组成的一条向量&#xff0c;c是可以限制的&#xff0c;比如说权重大于等于0。 使用迭代优化算法求解一般只能保证找到局…

web开发学习笔记(12.mysql数据库进阶)

1.多表查询 2.内连接 3.外连接&#xff0c;分为左外连接和右外连接 左外连接完全包含左表的数据和两个表的交集 右外连接完全包含右表的数据和两个表的交集 4.子查询&#xff0c;又成为嵌套查询 分为标量子查询和列子查询和行子查询 首先是标量子查询 列子查询 in, not in…

sqlilabs第六十一六十二关

Less-61(GET - challenge - Double Query- 5 queries allowed -Variation 4) 手工注入 报错注入 Less-62(GET - challenge - Bilnd- 130 queries allowed -Variation 1) 手工注入 过于复杂简单写写 这个应该是用不了只能用延时 自动注入 写个脚本就可以

Mysql-全局锁、表锁、行锁

本文已收录于专栏 《数据库》 目录 全局锁概述说明开启方式应用场景 表锁概念说明实现方式意向锁 开启方式 行锁概念说明实现方式记录锁&#xff1a;间隙锁临键锁 总结提升 全局锁 概述说明 全局锁是是对整个数据库实例加锁&#xff0c;加锁后整个实例就处于只读状态&#xff…

bat批处理基础教学

bat批处理基础教学 titleset命令模式-算数运算重定向运算多命令运算| 管道符--左边的结果作为右边的输入接受外部参数 -%1注释 rem命令公共介绍- /?时间命令 date time启动 start调用其他bat文件 call计划任务查看环境变量 settelnet是远程端口命令,不加密判断ifpause>null…

Vue中$watch()方法和watch属性的区别

vue中$watch()和watch属性都是监听值的变化的&#xff0c;是同一个作用&#xff0c;但是有两个不同写法。 用法一&#xff1a; //注意&#xff1a;这种方法是监听不到对象的变化的。 this.$watch((newVal,oldVal)>{ }) 用法二&#xff1a; watch:{xxx:(newVal,oldVal)>…

66 C++对象模型探索。C++编译器在什么时候给我们创建默认的构造函数?

一 前提&#xff0c;关于C编译器给我们生成 默认构造函数 的错误认知 传统知识认为&#xff1a;如果在一个类中&#xff0c;我们没有定义任何的构造函数&#xff0c;那么编译器会为我们隐式自动定义一个默认的构造函数&#xff0c;我们称这种构造函数为 "合成的默认构造函…

ElasticSearch 7.x现网运行问题汇集3

问题描述 某现网ElasticSearch 故障&#xff0c;很长时间unassgined_shards的数量都不减少。 原因分析与解决方案&#xff1a; 先了解整体状态&#xff0c;使用Postman请求&#xff0c;如下几个请求命令&#xff1a; GET /_cat/indicesGET /_cat/shardsGET /_cluster/health…

Minio搭建和使用

目前可用于文件存储的网络服务选择也有不少&#xff0c;比如阿里云OSS、七牛云、腾讯云等等&#xff0c;可是收费都有点小贵。为了节约成本&#xff0c;很多公司使用MinIO做为文件服务器。 MinIO是一个开源的分布式对象存储服务器&#xff0c;支持S3协议并且可以在多节点上实现…

Conmi的正确答案——使用eclipse进行ESP32C3的debug

eclipse IDE 版本&#xff1a;2023-12 1、安装debug环境 参考大神的教程&#xff1a;【图文】手把手教你使用 Eclipse IDE 开发 ESP32 &#xff08;这里是为了我下次回来速通才写的部分&#xff09; 1.1、安装插件&#xff08;plug-in&#xff0c;新的软件已经写成software了…

Docker安装启动、常用命令、应用部署、迁移备份、Dockerfile、Docker私有仓库

目录 1.Docker安装与启动 1.1 安装Docker 1.2 设置ustc的镜像 1.3 Docker的启动与停止 2.常用命令 2.1 镜像相关命令 2.1.1 查看镜像 2.1.2 搜索镜像 2.1.3 拉取镜像 2.1.4 删除镜像 2.2 容器相关命令 2.2.1 查看容器 2.2.2 创建与启动容器 2.2.3 停止与启动容器 2.…

仿真机器人-深度学习CV和激光雷达感知(项目2)day5【作业1与答案1】

文章目录 前言作业1答案1 前言 &#x1f4ab;你好&#xff0c;我是辰chen&#xff0c;本文旨在准备考研复试或就业 &#x1f4ab;本文内容是我为复试准备的第二个项目 &#x1f4ab;欢迎大家的关注&#xff0c;我的博客主要关注于考研408以及AIoT的内容 &#x1f31f; 预置知识…

Java - 单元测试及Junit的使用讲解及练习

目录 &#x1f436;2.1 什么是单元测试 &#x1f436;2.2 测试分类&#xff1a; 1. &#x1f959;黑盒测试 2. &#x1f959;白盒测试 &#x1f436;2.3 Junit介绍 &#x1f436;2.4 Junit的基本使用步骤:(默认使用maven创建项目) 1. &#x1f959;pom.xml中引入依赖: 2…

考试查分场景重保背后,我们如何进行可用性测试

作者&#xff1a;暮角 随着通过互联网音视频与知识建立连接的新学习方式在全国范围内迅速普及&#xff0c;在线教育/认证考试的用户规模呈井喷式增长。但教育容不得半点马虎与妥协&#xff0c;伴随用户规模不断增长&#xff0c;保证系统稳定性、有效避免千万考生考试时遭遇故障…

14 STM32 - IIC (时序图+软件源码)

14.1 IIC简介 IIC&#xff08;Inter-Integrated Circuit&#xff09;&#xff0c;中文集成电路总线&#xff0c;是一种串行通信总线&#xff0c;使用多主从架构。I2C串行总线一般有两根信号线&#xff0c;一根是双向的数据线SDA&#xff0c;另一根是时钟线SCL。所有接到I2C总线…

目标检测 - 绘制bounding box

工具类 from PIL.Image import Image, fromarray import PIL.ImageDraw as ImageDraw import PIL.ImageFont as ImageFont from PIL import ImageColor import numpy as npSTANDARD_COLORS [AliceBlue, Chartreuse, Aqua, Aquamarine, Azure, Beige, Bisque,BlanchedAlmond, …

C++提高编程---模板---类模板

目录 一、类模板 1.模板 2.类模板的作用 3.语法 4.声明 二、类模板和函数模板的区别 三、类模板中成员函数的创建时机 四、类模板对象做函数参数 五、类模板与继承 六、类模板成员函数类外实现 七、类模板分文件编写 八、类模板与友元 九、类模板案例 一、类模板 …

SpringBoot - SpringBoot手写模拟SpringBoot启动过程

依赖 建一个工程&#xff0c;两个Module: 1. springboot模块&#xff0c;表示springboot框架的源码实现 2. user包&#xff0c;表示用户业务系统&#xff0c;用来写业务代码来测试我们所模拟出来的SpringBoot 首先&#xff0c;SpringBoot是基于的Spring&#xff0c;所以我…

3DMAX初级小白班第一课:菜单栏介绍

基本介绍 这里不可能一个一个选项全部教给大家&#xff08;毕竟之后靠实操慢慢就记住了&#xff09;&#xff0c;只说一些相对需要注意的设置。 自定义-热键编辑器-热键设置 这里有你所需要的全部快捷键 自定义-自定义UI启动布局 将UI布局还原到启动的位置 自定义-通用单…