
简介这份源码面向Python初学者与需要教学辅助工具的教师提供一套匿名考试成绩登记与学情分析系统的完整实现。项目以Python为核心结合HTML、JavaScript与CSS构建前后端交互界面并借助SQLite3数据库完成成绩数据的持久化存储同时通过日志配置与匿名化处理兼顾隐私保护。压缩包共24个文件包含6个HTML页面、5个Python脚本、5个JavaScript文件、2个CSS样式文件以及数据库、配置、字体与说明文档等整体约1.64MB结构清晰便于按模块阅读。已有303人学习下载。读者可从中掌握数据处理、事件分发、主控逻辑与工具函数的分层写法理解前端动态加载与即时反馈的实现方式并参考数据库设计与日志排错思路快速搭建可运行的班级成绩管理与学情分析工具。1. 匿名成绩登记系统一份能直接跑起来的 Python 学情分析源码期末成绩出来班主任想按分数段看班级掌握情况又不想把学生姓名和分数绑在一起传阅。这个基于 Python 的匿名考试成绩登记与学情分析系统源码解决的正是这个场景学生用匿名标识填分后端落库前端出统计。整套包 24 个文件后端 5 个 Python 脚本main.py、handle.py、daemon.py、dbm.py、utils.py前端 6 个 HTML 页面配 5 个 JS 和 2 个 CSS数据落在 SQLite 的 xqing.db3 里日志走 conf_logging.json。适合会一点 Python、想拿一个前后端闭环小项目练手的人也适合教师做班级学情看板。下面按「是什么 → 怎么跑 → 坑在哪 → 怎么改」拆开讲。2. 拆开源码包五个 Python 脚本各管什么2.1 从文件清单反推模块职责拿到一个源码包我习惯先看文件命名再读代码命名往往暴露了作者的分层思路。这份包里的 Python 脚本分工相当清晰文件职责依赖关系main.py程序入口启动服务、注册路由调用 handle、dbmhandle.py请求处理登录、登记、查询的分发调用 dbm、utilsdaemon.py后台常驻逻辑定时任务或连接维持调用 dbmdbm.py数据库封装SQLite 的增删改查操作 xqing.db3utils.py工具函数加密、格式化、校验被其他脚本引用前端部分login.html 负责登录fillin.html 是成绩录入页mgr.html 和 mgr_exam.html 是管理端list.html 出列表main.html 是主框架。JS 里 md5.js 说明密码或匿名标识做了哈希element-ui-index.js 和 vue.js 说明前端用了 Vue Element UI 这套组合axios.min.js 负责和后端通信jquery.min.js 大概率是历史遗留或给老代码兜底。提示先读 readme.txt 和 LICENSE确认授权范围再动手改别上来就删文件。2.2 匿名机制落在哪一层「匿名」这个词容易让人以为做了多复杂的隐私计算实际看文件构成最可能的实现是学生端不采集真实姓名只用一个系统生成的编号或学生自填的代号配合 md5.js 做一次哈希后入库。这样数据库里存的是哈希值加分数即使 db3 文件泄露也无法直接反查是谁。常见做法是在 utils.py 里放一个 hash 函数handle.py 在写入前调用它。如果你要增强可以在这一层加盐salt盐值放 conf_logging.json 之外的独立配置里别和日志配置混在一起。这里要提醒一句md5 本身不适合做安全哈希练手够用真上生产建议换 hashlib 的 sha256 加盐。2.3 数据库表结构怎么读xqing.db3 是 SQLite 文件不用装服务直接用命令行或 DB Browser 打开就能看表。先确认表名和字段# 用 sqlite3 命令行打开数据库查看所有表 sqlite3 xqing.db3 .tables # 查看某张表的结构假设表名叫 score sqlite3 xqing.db3 .schema score第一条命令列出库里所有表第二条打印建表语句你能看到字段名、类型和约束。一般这类系统至少有两张表一张存匿名标识和成绩一张存考试科目或场次。看清字段类型很关键比如分数是 INTEGER 还是 REAL直接影响后面统计时要不要做类型转换。如果 .tables 报错说文件不是数据库多半是 db3 被当文本改过或者路径不对。3. 把项目跑起来环境、依赖与启动顺序3.1 Python 环境与依赖确认这套代码是纯 Python 加前端静态资源没有 requirements.txt说明依赖不多。先确认 Python 版本建议 3.8 以上# 查看当前 Python 版本 python --version # 或 python3 --version如果版本低于 3.6f-string 之类的语法可能报错。接着看 main.py 顶部 import 了哪些第三方库常见的是 flask 或 http.server 这类轻量方案。如果 import 里有 flask就装一下# 安装 Web 框架按 main.py 实际 import 调整 pip install flask装完再跑别一次装一堆用不上的库。前端那几个 JS 是本地文件不需要 npm浏览器直接加载这点对新手友好。3.2 启动服务与访问页面启动顺序上先确认数据库文件在位再起服务# 进入项目根目录 cd 项目目录 # 启动主程序 python main.py跑起来后终端一般会打印监听地址常见是 127.0.0.1:5000 或 8000。浏览器打开对应地址先看到 login.html登录后进 fillin.html 录分再去 list.html 看结果。如果终端报端口占用改 main.py 里的端口号或者先杀掉占用进程。注意conf_logging.json 里的日志路径如果是绝对路径换机器会写不进去先改成相对路径或确认目录存在。3.3 日志配置怎么调conf_logging.json 控制日志级别、格式和输出位置。调试阶段把级别调到 DEBUG能看到每个请求的处理细节{ version: 1, handlers: { file: { class: logging.FileHandler, filename: app.log, level: DEBUG } }, root: { level: DEBUG, handlers: [file] } }version 是配置格式版本固定写 1handlers 定义输出目标filename 是日志文件名level 控制记录粒度root 是根日志器level 决定全局最低记录级别。改完重启生效。如果日志文件一直空检查 filename 的目录是否有写权限以及代码里有没有真的调用 logging。4. 学情分析怎么算从原始分数到班级看板4.1 统计指标的 SQL 写法学情分析的核心是把原始分数聚合成有意义的指标平均分、最高最低分、及格率、分数段分布。这些用 SQL 就能算不用把数据全拉到 Python 里循环。以一张 score 表字段anon_id、subject、score为例-- 各科目平均分、最高分、最低分 SELECT subject, AVG(score) AS avg_score, MAX(score) AS max_score, MIN(score) AS min_score, COUNT(*) AS cnt FROM score GROUP BY subject; -- 分数段分布按 10 分一档统计人数 SELECT CAST(score / 10 AS INT) * 10 AS bucket, COUNT(*) AS num FROM score GROUP BY bucket ORDER BY bucket;第一条按科目分组算聚合值GROUP BY 是分组关键COUNT(*) 统计每科人数。第二条用整除把分数归到 10 分档CAST 保证结果是整数bucket 就是分数段下界。这两条查询直接喂给前端图表比在 Python 里手写循环快也清晰。4.2 把统计结果接到前端后端算完通过一个接口把 JSON 吐给前端前端用 Vue 渲染。handle.py 里加一个统计路由返回结构大致是# handle.py 中新增统计接口的示意 import json from dbm import query_stats # 假设 dbm 里封装了查询 def stats_handler(request): # 调用数据库层拿到聚合结果 rows query_stats() # 转成前端友好的结构 data [{subject: r[0], avg: round(r[1], 2)} for r in rows] return json.dumps(data, ensure_asciiFalse)query_stats 是 dbm.py 里封装的查询函数返回元组列表列表推导把每行转成字典round 保留两位小数ensure_asciiFalse 保证中文科目名不被转义。前端 axios 拿到这个数组绑到 Element UI 的表格或图表组件上即可。改字段名时前后端要同步否则前端取不到值会显示空白。4.3 匿名标识的生成与校验匿名标识不能重复否则统计会串。常见做法是用 uuid 或时间戳加随机数生成写入前查一次库确认没冲突import uuid def gen_anon_id(): # 生成短一点的匿名标识取 uuid 前 8 位 return uuid.uuid4().hex[:8]uuid4 基于随机数碰撞概率极低取前 8 位够用且好记。如果要求可读性更强可以换成「班级码 序号」的组合但序号要保证唯一得靠数据库唯一索引兜底。建表时给 anon_id 加 UNIQUE 约束插入冲突时捕获异常重试这是最稳的后悔药。5. 避坑与排查跑不起来时先看这几处5.1 数据库文件打不开或表不存在现象启动后报 no such table或者 sqlite3 提示 file is not a database。原因通常是 db3 文件路径不对或者文件被编辑器当文本保存过导致头部损坏。解决先用 sqlite3 xqing.db3 .tables 确认能读读不了就从备份恢复或者按 dbm.py 里的建表语句重建。路径问题检查 dbm.py 里连接时用的相对路径换目录跑时相对路径会变。5.2 前端页面白屏或样式错乱现象打开 HTML 只有文字没样式或者 Vue 没渲染。原因多半是静态资源路径写死成绝对路径或者 element-ui-index.js 加载顺序在 vue.js 之前。解决确认 vue.js 先于 element-ui 加载检查 HTML 里 script 和 link 的 src 是不是相对路径。浏览器 F12 看 Network 面板哪个文件 404 一目了然。5.3 端口占用或服务起不来现象终端报 Address already in use。原因是上次进程没退干净或端口被别的程序占了。解决换端口最省事改 main.py 里的端口号或者查占用进程杀掉。Linux 下 lsof -i:端口号Windows 下 netstat -ano | findstr 端口号找到 PID 再处理。5.4 日志不输出或乱码现象app.log 是空的或者中文变问号。原因是日志级别设太高把记录过滤了或者文件编码不是 utf-8。解决把 level 调到 DEBUGFileHandler 加 encodingutf-8 参数。conf_logging.json 里如果没写 encoding默认可能跟系统走Windows 上容易出乱码。5.5 匿名标识重复导致统计串号现象同一个匿名标识出现多条记录平均分算出来不对。原因是生成逻辑没做唯一校验或者并发写入时撞了。解决给 anon_id 加 UNIQUE 索引插入时捕获 IntegrityError 重新生成并发场景下用数据库自增主键做真正的唯一键匿名标识只作展示用。6. 二次开发把匿名登记改成可配置的学情看板跑通之后这套源码最值得改的地方是「匿名」和「分析」两个维度。匿名这块我一般会把哈希逻辑从 md5 换成 sha256 加盐盐值单独放一个 config 文件不跟日志配置混。改的时候注意已经入库的旧数据哈希值对不上要么清库重来要么写个迁移脚本统一重算。分析这块原始 SQL 只出了平均分和分数段实际教学里更关心的是「哪些知识点失分集中」。如果 fillin.html 能按题号录分score 表加一个 question_id 字段就能按题统计正确率-- 按题号统计正确率假设满分字段是 full_score SELECT question_id, AVG(score * 1.0 / full_score) AS correct_rate FROM score GROUP BY question_id ORDER BY correct_rate ASC;correct_rate 越低说明该题越难教师讲评时优先讲。score * 1.0 是为了避免整数除法SQLite 里整数相除会截断这个坑我踩过不止一次。验证改动是否生效别只看页面直接查库对数字改完统计逻辑后手动算一遍某个科目的平均分和页面显示比对对不上就查 SQL。我习惯每次改完统计口径都拿一个班的小样本手算校验确认无误再全量跑。这套源码结构不复杂适合拿来练「后端聚合 前端展示」的完整链路改两轮下来对 SQLite 和 Vue 的配合会熟很多。希望帮到你。本文还有配套的精品资源点击获取