|
|
преди 1 седмица | |
|---|---|---|
| .. | ||
| README.md | преди 1 седмица | |
| audit_mps_score_quota_2026.py | преди 1 седмица | |
| audit_mps_score_school_quota_totals_2026.py | преди 1 седмица | |
| fix_mps_score_school_quota_2026_bad_targets.py | преди 1 седмица | |
| fix_mps_score_school_quota_problems_2026.py | преди 2 седмици | |
| import_mps_score_2026.py | преди 2 седмици | |
| import_mps_score_quota_2026.py | преди 2 седмици | |
| import_mps_score_quota_manual_2026.py | преди 2 седмици | |
| import_mps_score_school_quota_2026.py | преди 2 седмици | |
| import_mps_score_school_quota_hongkou_2026.py | преди 2 седмици | |
| import_mps_score_school_quota_supplement_2026.py | преди 2 седмици | |
| mps_score_school_quota_2026_bad_targets_backup.json | преди 1 седмица | |
| mps_score_school_quota_2026_problems.json | преди 2 седмици | |
| mps_score_school_quota_2026_qingpu_reparse_backup.json | преди 1 седмица | |
| mps_score_school_quota_2026_supplement_problems.json | преди 2 седмици | |
| research_mps_score_school_quota_2026.py | преди 1 седмица | |
| 需求.md | преди 1 седмица | |
本文档记录本项目每年从 PDF/图片整理上海中考招生计划、成绩,并导入 MySQL 表 kylx365_db.MPS_Score 的需求、步骤和注意事项。
当前已完成 2026 年“计划”中的 1、2、3:自主招生、名额到区、名额到校。
每年需要处理两大类数据。
一、计划
二、成绩
2026 年当前状态:
目标数据库:kylx365_db
核心表:
MPS_School:学校表,所有学校相关信息以此表为准。MPS_Score:计划与成绩表,所有导入结果写入此表。常用参照查询:
SELECT *
FROM kylx365_db.MPS_School
WHERE SchoolType1 = '高中';
SELECT *
FROM kylx365_db.MPS_Score
WHERE ScoreYear = '2025'
AND ScoreType = '名额到校'
AND DistrictID = 1;
数据库连接信息不要写入 README 或提交到仓库。当前脚本里使用本机已有配置和 PyMySQL 驱动连接,后续最好抽成单独的本地配置文件或环境变量。
1 黄浦区
2 徐汇区
3 长宁区
4 静安区
5 普陀区
6 虹口区
7 杨浦区
8 闵行区
9 宝山区
10 嘉定区
11 浦东新区
12 金山区
13 松江区
14 青浦区
15 奉贤区
16 崇明区
计划类导入一般只写计划数,不写成绩。
通用字段规则:
ScoreYear:年份,例如 2026ScoreType:自主招生、名额到区、名额到校、1-15志愿DistrictID:对应区 IDSchoolTarget:高中学校 MPS_School.ID,以字符串写入SchoolFullName:必须使用高中 ID 对应的 MPS_School.SchoolFullNamePlanNum:计划人数ScoreTotal、Score1、Score2、Score3、Score4:计划导入时填 0ScoreTotalDifferenceValue:计划导入时填 0PlanNumDifferenceValue:当前计划数减去上一年同维度计划数OrderID:当前计划导入填 0SchoolNumber、SchoolNumber2:当前计划导入填空字符串SchoolOfGraduation1:当前计划导入填 "0"名额到校额外规则:
SchoolOfGraduation:初中学校 MPS_School.IDSchoolFullNameJunior:必须使用初中 ID 对应的 MPS_School.SchoolFullNameScoreYear + ScoreType + DistrictID + SchoolOfGraduation + SchoolTarget 理解。SchoolFullNameJunior 或 SchoolFullName。自主招生额外规则:
1学科2体育3艺术4国际(本市)5国际(非本市)SchoolTargetRemark2 可参考上一年同学校同类别备注;体育/艺术通常沿用“市级优秀体育学生”“市级艺术骨干学生”等说明。名额到区额外规则:
SchoolOfGraduation = 0SchoolFullNameJunior = NULLSchoolTargetRemark = ""每一类数据都按以下流程做:
MPS_Score 总行数、总计划数、分区汇总。MPS_School 后,再运行补录脚本,只补缺失行,并刷新问题清单。重要原则:
MPS_School,再重新匹配导入。优先级:
SchoolFullName 匹配。SchoolShortName、SchoolOtherName 匹配。学校名称常见问题:
本次经验:
SchoolOtherName 很适合放改名后的现名或曾用名。2026 懿德中学问题复盘:
上海市浦东新区懿德中学 时发现名额到校目标高中不对。上海市浦东复旦附中分校 和 上海中学东校,旧脚本分别写成了 复旦大学附属中学 和 上海市上海中学。上海市浦东复旦附中分校 先命中 复旦附中,上海中学东校 先命中 上海中学,导致分校/东校被主校抢走。华二普陀、宝山 华二宝山 / 上师附中宝山、浦东 上海中学东校 / 浦东复旦附中分校 / 华二临港奉贤分校、松江 松江二中 / 华二松江分校、奉贤 华二临港奉贤分校。华二 抢在 华二普陀 前面。pdfplumber.extract_tables() 的表格状态续接。extract_text() 的自然文本顺序识别高中段落,会把视觉上同行的内容拆错。例如 PDF 表格中 102056 上海交通大学附属中学 / 181021 上海市青浦区思源中学 / 1 是同一行,但文本抽取顺序会先输出 181021 上海市青浦区思源中学 1,再输出 102056 上海交通大学附属中学,导致思源中学被错误归到上一段 上海市上海中学。pdfplumber.find_tables() 的行坐标作为主依据,再用左侧高中代码文字的坐标判断“从哪一行开始切换高中”。这样可以处理页 1 同行高中代码、页 2 跨页延续、页 3/页 4 中途切换高中段落等情况。上海市上海中学 只对应 上海市青浦区凤溪中学;上海交通大学附属中学 对应 上海市青浦区思源中学 和 上海市青浦区实验中学。mps_score_school_quota_2026_bad_targets_backup.json,再重建普陀、宝山、浦东、松江、青浦、奉贤 6 个区数据;后续又单独备份青浦旧数据到 mps_score_school_quota_2026_qingpu_reparse_backup.json,并重建青浦区数据。ScoreYear + ScoreType + DistrictID + SchoolOfGraduation + SchoolTarget 不应重复。脚本分为三类:主流程脚本、公共解析/补录脚本、2026 一次性补充脚本。后续年度工作时,主流程和公共脚本可以复制改年份;一次性补充脚本主要用于追溯 2026 的特殊处理,不建议直接运行到新年份。
自主招生:
import_mps_score_2026.pyScoreType = '自主招生'。名额到区:
import_mps_score_quota_2026.py--dry-run。import_mps_score_quota_manual_2026.py 做手工/OCR 补充。名额到区官方总表审核:
audit_mps_score_quota_2026.py:读取官方《2026全市高中名额到区招生计划统计表》PDF,并与数据库 2026 名额到区 按高中汇总结果比对。MPS_School.SchoolNumber,再比较官方计划数与数据库 SUM(PlanNum)。名额到校:
research_mps_score_school_quota_2026.py已支持编号匹配、全称/简称/别名匹配、括号内“现名”匹配、同区唯一包含式匹配。
import_mps_score_school_quota_2026.py
主导入脚本,读取 16 个区名额到校 PDF。
支持 --dry-run。
解析不确定的数据写入 mps_score_school_quota_2026_problems.json。
import_mps_score_school_quota_supplement_2026.py
用于补充处理徐汇、嘉定等表格/OCR特殊区。
import_mps_score_school_quota_hongkou_2026.py
用于处理虹口图片读图后的结构化数据。
fix_mps_score_school_quota_problems_2026.py
当 MPS_School 中新增/修正学校后,重新解析问题区并补插当前能匹配的数据。
会跳过数据库中已存在的 DistrictID + SchoolOfGraduation + SchoolTarget 组合。
会刷新 mps_score_school_quota_2026_problems.json,只保留仍未解决的问题。
2026 一次性补充脚本:
import_mps_score_quota_manual_2026.py:用于 2026 名额到区图片/OCR特殊区的手工补录,不是新年份通用入口。import_mps_score_school_quota_hongkou_2026.py:用于 2026 虹口名额到校图片读图后的手工矩阵导入,不是新年份通用入口。import_mps_score_school_quota_supplement_2026.py:包含 2026 徐汇手工矩阵和嘉定特殊 PDF 解析;其中 collect_jiading 目前仍被 fix_mps_score_school_quota_problems_2026.py 引用,所以不要单独删除。生成物:
__pycache__/ 和 *.pyc 是 Python 运行缓存,不属于业务数据或脚本,已在主仓库 .gitignore 中忽略。名额到校区内合计审核:
audit_mps_score_school_quota_totals_2026.py:读取各区名额到校 PDF 中明确存在的“合计”行,与数据库 2026 名额到校 按区/高中汇总结果比对。计划/自主招生:
ScoreYear = 2026ScoreType = 自主招生计划/名额到区:
ScoreYear = 2026ScoreType = 名额到区计划/名额到校:
ScoreYear = 2026ScoreType = 名额到校mps_score_school_quota_2026_problems.json 已清空修正记录:
mps_score_school_quota_2026_bad_targets_backup.json。2026 名额到校最终分区汇总:
| DistrictID | 区 | 行数 | 计划数 |
|---|---|---|---|
| 1 | 黄浦区 | 217 | 996 |
| 2 | 徐汇区 | 221 | 899 |
| 3 | 长宁区 | 63 | 418 |
| 4 | 静安区 | 271 | 1102 |
| 5 | 普陀区 | 179 | 736 |
| 6 | 虹口区 | 80 | 488 |
| 7 | 杨浦区 | 144 | 707 |
| 8 | 闵行区 | 460 | 1290 |
| 9 | 宝山区 | 343 | 1076 |
| 10 | 嘉定区 | 130 | 612 |
| 11 | 浦东新区 | 1260 | 2095 |
| 12 | 金山区 | 56 | 355 |
| 13 | 松江区 | 190 | 779 |
| 14 | 青浦区 | 98 | 766 |
| 15 | 奉贤区 | 131 | 345 |
| 16 | 崇明区 | 50 | 223 |
总量:
SELECT COUNT(*) AS c, SUM(PlanNum) AS total
FROM MPS_Score
WHERE ScoreYear = '2026'
AND ScoreType = '名额到校';
分区:
SELECT DistrictID, COUNT(*) AS c, SUM(PlanNum) AS total
FROM MPS_Score
WHERE ScoreYear = '2026'
AND ScoreType = '名额到校'
GROUP BY DistrictID
ORDER BY DistrictID;
检查某区上一年参照:
SELECT *
FROM MPS_Score
WHERE ScoreYear = '2025'
AND ScoreType = '名额到校'
AND DistrictID = 1
ORDER BY ID;
查初中学校名称:
SELECT ID, DistrictID, SchoolNumber, SchoolFullName, SchoolShortName, SchoolOtherName
FROM MPS_School
WHERE SchoolType1 = '初中'
AND (
SchoolFullName LIKE '%学校名关键词%'
OR SchoolShortName LIKE '%学校名关键词%'
OR SchoolOtherName LIKE '%学校名关键词%'
);
把脚本从 2026 复制到新年份后,至少检查这些常量:
YEARPREVIOUS_YEARBASE_DIR导入前必须确认目标年份目标类型没有已有数据,或脚本明确支持跳过/补录。
不要为了重新跑脚本而删除数据库旧数据,除非明确确认要重做且已备份。
计划/1-15 志愿:
ScoreType、字段、维度和差值计算。成绩导入:
ScoreTotal、Score1、Score2、Score3、Score4 等字段,不能沿用计划类全部填 0 的规则。