简介:这是一套面向高考考生、家长及教育信息化开发者的志愿填报参考系统源码,基于Python与Django构建Web服务,可按高校城市、高考排名、高校层次、专业等条件检索匹配的院校与专业信息,帮助使用者快速定位志愿填报方向。资源包共132个文件,约3.38MB,涵盖14个py业务脚本、15个js与12个css前端资源、5个html模板、3个sql数据库文件,以及xlsx与xlsm等Excel数据源,另含图片、字体、Dockerfile与readme说明,结构完整便于二次开发。项目包含近年高考录取数据自动化录入模块,通过Python读取Excel写入数据库,并配套Docker部署方案,可快速启动服务。目前已有571人学习下载,适合具备Python与Django基础、希望研究数据入库与Web检索实现的开发者参考借鉴。
1. 高考志愿填报参考系统:一份能跑起来的 Python + SQL 全栈练手项目
每年六月,总有亲戚朋友来问「能不能帮忙看看志愿怎么填」。市面上的填报 App 要么收费,要么数据不透明,想自己搭一个又不知道从哪下手。这份基于 Python 实现的高考志愿填报参考系统源码,配了一套 SQL 数据库文件,正好卡在这个需求点上——它不是玩具级的增删改查,而是把院校信息、专业分数线、位次换算、冲稳保推荐这几条主线串成了一个能实际运行的 Web 系统。技术栈是 Python 后端加关系型数据库,前端走模板渲染,没有花哨的前后端分离,对刚学完 Python 基础语法、想找一个完整项目练手的人来说,门槛刚好。适合三类人:一是计算机专业做课程设计的学生,二是想转行做后端开发、需要一个真实业务场景练手的初学者,三是中学里负责信息化建设的老师,想自己维护一套校内参考工具。源码包和数据库文件都在压缩包里,解压后按下面的步骤走,半小时内能看到登录页。
2. 环境搭建与数据库初始化:从 python 安装到 sql 新建数据库
2.1 运行环境选型与依赖清单
这套源码对 Python 版本没有苛刻要求,3.8 到 3.11 都能跑,我实测用的是 3.10。数据库方面,源码里附的是.sql备份文件,MySQL 5.7 和 8.0 都兼容,如果你机器上装的是 SQL Server 2012 的备份想直接还原到 2008,那大概率会翻车——版本向下兼容这事在数据库领域基本不存在,所以老老实实用 MySQL 或者 SQLite 最省事。依赖库不多,核心就四个:Flask 做 Web 框架,SQLAlchemy 做 ORM 映射,PyMySQL 做驱动,Werkzeug 处理密码哈希。如果你习惯用 vscode 配置 python 环境,直接在项目根目录建虚拟环境就行。
# 创建虚拟环境,避免污染全局包 python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate # 安装依赖,建议逐条装,方便定位是哪个包报错 pip install flask pip install sqlalchemy pip install pymysql pip install werkzeug这里有个细节:不要图省事直接pip install -r requirements.txt,因为不同版本的 Flask 和 SQLAlchemy 之间有兼容矩阵,源码作者写依赖文件时的版本号未必和你本地 Python 版本匹配。逐条装的好处是,一旦某个包编译失败,你能立刻知道是哪个环节的问题。装完后用pip list确认一下版本,Flask 建议 2.0 以上,SQLAlchemy 建议 1.4 以上。
2.2 数据库建库与导入 SQL 文件
数据库这块是整个项目的地基。源码包里一般会有一个database文件夹,里面放着.sql文件。常见做法是先用命令行或者图形化工具新建一个空数据库,再把 SQL 文件导入进去。注意字符集一定要选utf8mb4,否则院校名称里的生僻字会变成问号。
-- 新建数据库,字符集用 utf8mb4 支持完整 Unicode CREATE DATABASE gaokao_reference DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 切换到该数据库 USE gaokao_reference; -- 导入前先确认 SQL 文件编码是 UTF-8,用 source 命令导入 -- 在 MySQL 命令行中执行: -- source /path/to/your/database.sql;导入完成后,用SHOW TABLES;看一眼表结构。通常会有university(院校表)、major(专业表)、score_line(分数线表)、user(用户表)这几张核心表。如果导入时报「sql数据库显示正在装载」这类提示,多半是文件太大或者索引重建中,等几分钟再查。要是导入到一半报外键约束错误,说明 SQL 文件里的表创建顺序有问题,先禁用外键检查SET FOREIGN_KEY_CHECKS=0;再导入,导完再打开。
2.3 配置文件修改与首次启动
源码里一般会有一个config.py或者settings.py,里面写着数据库连接串。你需要把用户名、密码、数据库名改成自己本地的。
# config.py 关键配置项 import os class Config: # 数据库连接串,格式:mysql+pymysql://用户名:密码@地址:端口/库名 SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:yourpassword@localhost:3306/gaokao_reference' # 关闭对象修改追踪,省内存 SQLALCHEMY_TRACK_MODIFICATIONS = False # 会话密钥,用于登录状态加密,随便改成一串随机字符 SECRET_KEY = 'change-this-to-a-random-string'改完配置后,在项目根目录执行python app.py或者flask run。如果看到Running on http://127.0.0.1:5000,说明服务起来了。浏览器打开这个地址,应该能看到登录页或者首页。如果报ModuleNotFoundError,检查虚拟环境是否激活;如果报数据库连接拒绝,检查 MySQL 服务是否启动、端口是否被占用。第一次跑建议把调试模式打开,方便看错误堆栈。
3. 核心功能模块拆解:分数线查询、位次换算与冲稳保推荐逻辑
3.1 院校与专业数据模型设计
这套系统的数据模型不算复杂,但设计上有几个值得注意的点。院校表和专业表是一对多关系,分数线和专业表也是一对多,因为同一个专业在不同年份的录取分数不同。用户表单独存,和业务数据解耦。理解这个结构,后面改代码或者加功能才不会迷路。
| 表名 | 核心字段 | 说明 |
|---|---|---|
| university | id, name, province, level, type | 院校基本信息,level 区分 985/211/双一流 |
| major | id, university_id, name, category | 专业信息,外键关联院校 |
| score_line | id, major_id, year, min_score, min_rank | 历年分数线,位次是关键字段 |
| user | id, username, password_hash, role | 用户表,role 区分普通用户和管理员 |
位次字段min_rank是这套系统的灵魂。高考志愿填报的核心逻辑不是看分数,而是看位次,因为每年试卷难度不同,分数会浮动,但位次相对稳定。源码里如果做了位次换算功能,那说明作者是懂行的。你拿到源码后,重点看score_line表的查询逻辑,这是整个推荐算法的数据基础。
3.2 分数线查询与位次换算的实现
查询功能看起来简单,但要做到「输入分数和省份,返回可报院校列表」,中间涉及好几层过滤。常见做法是先用省份筛选院校,再用分数区间筛选专业,最后按位次排序。下面这段代码是典型的查询逻辑,我按源码风格还原了一下。
# 查询某省份、某分数段内的可报院校 def query_schools(province, score, year): # 先根据省份和年份拿到该省的控制线,用于计算线差 control_line = get_control_line(province, year) # 线差 = 考生分数 - 批次控制线 score_diff = score - control_line # 查询近三年录取线差在考生线差上下浮动 20 分以内的专业 results = db.session.query( University.name, Major.name, ScoreLine.min_score, ScoreLine.min_rank ).join(Major, University.id == Major.university_id)\ .join(ScoreLine, Major.id == ScoreLine.major_id)\ .filter( University.province == province, ScoreLine.year.in_([year-1, year-2, year-3]), ScoreLine.min_score.between(score - 20, score + 20) ).order_by(ScoreLine.min_rank.asc()).all() return results这段代码的逻辑说明:先算线差,再用线差去匹配历史数据,而不是直接用分数匹配。参数score是考生分数,province是考生所在省份,year是当前年份。between(score - 20, score + 20)这个区间可以调整,冲的院校往上加 10 到 15 分,稳的院校在正负 5 分以内,保的院校往下减 10 到 20 分。如果你想让推荐更精准,可以把线差换成位次差,逻辑一样,只是把min_score换成min_rank。
3.3 冲稳保推荐策略的参数调整
冲稳保是志愿填报的经典策略,但很多人不知道具体怎么分档。源码里如果实现了这个功能,通常会有一个recommend函数,按位次百分比来划分。我一般会这样设:冲的院校位次在你当前位次的 80% 到 100% 之间,稳的在 100% 到 120% 之间,保的在 120% 到 150% 之间。这个比例不是固定的,要根据省份竞争激烈程度微调。
# 冲稳保分档推荐 def recommend_by_rank(user_rank, all_schools): chong = [] # 冲:位次要求比考生稍高 wen = [] # 稳:位次要求与考生匹配 bao = [] # 保:位次要求比考生低 for school in all_schools: ratio = school.min_rank / user_rank if 0.8 <= ratio < 1.0: chong.append(school) elif 1.0 <= ratio <= 1.2: wen.append(school) elif 1.2 < ratio <= 1.5: bao.append(school) return {'冲': chong, '稳': wen, '保': bao}参数user_rank是考生的全省位次,school.min_rank是该专业去年录取的最低位次。ratio小于 1 说明学校要求比考生高,属于冲;大于 1 说明考生有优势,属于保。实际使用时,冲的志愿不要超过总志愿数的三分之一,保的志愿至少留两个,这是血泪经验——每年都有考生全填冲的,最后滑档。
4. 避坑与常见问题排查:从数据库连接到页面渲染的五个翻车点
4.1 数据库连接报错 1045 或 2003
现象:启动服务时报Access denied for user 'root'@'localhost'或者Can't connect to MySQL server。原因通常是密码写错、MySQL 服务没启动、或者端口不是默认的 3306。解决:先确认 MySQL 服务在运行,Windows 在服务管理器里看,macOS 用brew services list,Linux 用systemctl status mysql。然后检查config.py里的密码,如果密码里有特殊字符比如@或#,需要做 URL 编码。端口被占用的话,改成 3307 再试。
4.2 导入 SQL 文件时字符集乱码
现象:导入后院校名称显示为乱码,或者查询时中文变成问号。原因:SQL 文件本身是 GBK 编码,而数据库是 utf8mb4,两边不一致。解决:用file -i database.sql查看文件编码,如果是 GBK,先用iconv -f GBK -t UTF-8 database.sql > database_utf8.sql转码,再导入。或者在导入命令前加SET NAMES utf8mb4;。这个问题在 Windows 上尤其常见,因为记事本默认保存编码可能是 GBK。
4.3 Flask 启动后页面空白或 500 错误
现象:浏览器打开是空白页,或者报 Internal Server Error。原因:模板文件路径不对、静态资源没加载、或者数据库查询返回了 None 导致后续代码报错。解决:先看终端有没有 Python 报错堆栈,Flask 调试模式下会打印详细错误。如果是模板问题,检查templates文件夹是否在项目根目录,文件名是否和render_template里的参数一致。如果是数据库查询问题,在查询后面加try...except把异常打出来,常见的是表名写错或者字段不存在。
4.4 位次换算结果偏差过大
现象:推荐的院校和实际录取情况差距很大。原因:用了分数直接匹配,没有换算位次;或者用了太久远的数据,比如只用了五年前的分数线。解决:确认score_line表里有近三年的数据,查询时用year.in_([year-1, year-2, year-3])限制年份。另外,新高考省份的位次换算和传统文理科不一样,如果源码没有做选科适配,那在新高考省份用会不准,需要自己改逻辑。
4.5 用户登录状态丢失或密码验证失败
现象:登录后刷新页面就退出,或者明明密码对却提示错误。原因:SECRET_KEY没设置或者每次重启都变,导致会话失效;密码哈希算法不匹配,比如注册时用了pbkdf2但验证时用了md5。解决:把SECRET_KEY写死成一个固定随机串,不要用os.urandom(24)每次生成。密码这块统一用werkzeug.security的generate_password_hash和check_password_hash,不要自己写加密逻辑。
5. 二次开发与数据更新:把参考系统改成你自己的工具
5.1 批量导入最新分数线数据
源码自带的数据库文件通常是某一年的快照,想让它持续可用,得会自己更新数据。常见做法是写一个 Python 脚本,从 Excel 或者 CSV 里读数据,批量插入score_line表。下面这个脚本我改过好几版,处理过几万条数据,比较稳。
import pandas as pd from sqlalchemy import create_engine # 数据库连接,和 config.py 保持一致 engine = create_engine('mysql+pymysql://root:yourpassword@localhost:3306/gaokao_reference') # 读取 Excel,假设列名为:院校名称、专业名称、年份、最低分、最低位次 df = pd.read_excel('score_data_2024.xlsx') # 逐行插入,用事务保证一致性 with engine.begin() as conn: for _, row in df.iterrows(): # 先查院校 id,没有就跳过 uni = conn.execute( "SELECT id FROM university WHERE name = %s", (row['院校名称'],) ).fetchone() if not uni: continue # 查专业 id major = conn.execute( "SELECT id FROM major WHERE university_id = %s AND name = %s", (uni[0], row['专业名称']) ).fetchone() if not major: continue # 插入分数线 conn.execute( "INSERT INTO score_line (major_id, year, min_score, min_rank) VALUES (%s, %s, %s, %s)", (major[0], row['年份'], row['最低分'], row['最低位次']) )这段代码的关键点:用engine.begin()开启事务,要么全成功要么全回滚,避免插一半失败留下脏数据。iterrows()虽然慢,但几万条数据以内可以接受,如果数据量超过十万条,建议用to_sql批量写入。参数row['院校名称']要和 Excel 列名完全一致,包括空格。插入前先查 id 是为了避免外键约束报错,如果院校或专业不存在,直接跳过而不是报错中断。
5.2 增加「专业就业前景」字段的完整流程
源码里的专业表只有名称和类别,想加就业前景字段,需要改三处:数据库加列、模型加属性、页面加展示。这是典型的二次开发流程,走一遍就熟悉了。
-- 第一步:数据库加列 ALTER TABLE major ADD COLUMN employment_rate DECIMAL(5,2) DEFAULT NULL COMMENT '就业率'; ALTER TABLE major ADD COLUMN avg_salary INT DEFAULT NULL COMMENT '平均起薪';# 第二步:模型加属性,在 models.py 的 Major 类里加两行 class Major(db.Model): __tablename__ = 'major' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100)) # 新增字段 employment_rate = db.Column(db.Numeric(5, 2)) avg_salary = db.Column(db.Integer)第三步是在模板文件里加展示逻辑,找到major_detail.html或者类似的模板,在专业名称下面加一行{{ major.employment_rate }}。改完后重启服务,如果页面报AttributeError,说明模型没同步,检查db.create_all()是否执行过。注意db.create_all()不会修改已有表结构,加字段必须用ALTER TABLE手动改。
5.3 用 SQL 视图简化复杂查询
当查询涉及三张以上表关联时,代码会变得很难维护。我一般会建一个视图,把常用的关联查询固化下来,Python 里直接查视图,代码清爽很多。
-- 创建视图,把院校、专业、分数线三表关联固化 CREATE VIEW v_school_major_score AS SELECT u.name AS university_name, u.province, u.level, m.name AS major_name, m.category, s.year, s.min_score, s.min_rank FROM university u JOIN major m ON u.id = m.university_id JOIN score_line s ON m.id = s.major_id;建完视图后,Python 里直接SELECT * FROM v_school_major_score WHERE province = '河南' AND year = 2023就行,不用再写三表 JOIN。视图的缺点是数据实时性依赖基表,基表更新视图自动更新,这个没问题。但如果视图嵌套太多层,查询性能会下降,建议视图只做一层关联,复杂逻辑还是放 Python 里。
5.4 数据备份与迁移的稳妥做法
系统跑起来后,数据就是资产,备份这事不能偷懒。我习惯用mysqldump每天定时导出一份,命令很简单,但参数要写对。
# 导出整个数据库,包含建表语句和数据 mysqldump -u root -p gaokao_reference > backup_$(date +%Y%m%d).sql # 只导出数据,不导出建表语句,适合表结构没变的情况 mysqldump -u root -p --no-create-info gaokao_reference > data_only.sql # 恢复时先建空库,再导入 mysql -u root -p gaokao_reference < backup_20240601.sql参数--no-create-info在表结构没变、只想更新数据时很有用,导入时不会报「表已存在」的错误。$(date +%Y%m%d)是 shell 的日期替换,生成的文件名带日期,方便回溯。恢复前一定要先建空库,否则导入会失败。如果数据量很大,加--single-transaction参数避免锁表。
从那以后我每次拿到新的源码包,都先跑一遍数据库导入和启动流程,确认能跑通再动代码。这套高考志愿填报系统不算复杂,但麻雀虽小五脏俱全,把环境搭建、数据导入、查询逻辑、推荐算法这几块吃透,再去看其他 Python Web 项目会轻松很多。希望帮到你。
本文还有配套的精品资源,点击获取