方法论
这一页说明数据是怎么从官方 PDF 变成可查询表格的,以及我们用什么办法确认它没被弄错。
一、采集:逐张官方 PDF / 逐份区级文件
- 对名额分配:从上海市教育考试院官网按栏目逐年枚举发布记录,定位每一年的发布页;官方发布形态逐年不同——有的是区码直链、有的是序号命名、2024 年甚至是「索引 PDF 里嵌链接注解」的形式,均逐一适配;下载全部 166 份 PDF 并留存原件。
- 对统一招生(2026):这类数据不在市级官网集中发布,而是分散在各区的门户、教育局、教育考试中心、招考网上,共 17 份文件,形式有 PDF 文字表、HTML 表格页、图片表三种。逐区定位并留存原件。
- 对图片表(徐汇 / 宝山 / 崇明):官方只发布了扫描图或整表截图,没有文字层,走 OCR 逐格识别,再用官方主数据表纠错(见下)。
二、解析:按坐标取数,不靠版面猜测
官方 PDF 的文字层在读取时,表头与数据行是分离的,直接按行读会把分数错位。本站按词坐标做两件事:
- 用 y 坐标把词聚成「表格行」;
- 用 x 坐标区分「校名 / 代码 / 各分数列」,避免把校名与数字混列。
其中有两处必须专门处理:
- 名额分配到校表的「初中学校」是合并单元格且垂直居中,名字的纵向位置与数据行相差约 5pt——容差不够时,整个初中名会被甩到相邻行,导致大面积缺失。
- 统招表里,有的区把一个逻辑行拆成 2–3 个 y 行(校名折行 / 代码单独一行 / 备注折行),而各区的真实行距又不一样(最密的松江只有 10.2pt、续行间距 6.7–7.4pt)。本站的做法是「强锚行 + 定距吸收续行」:只认「有招生代码」或「行内有数字录取分」的行作锚,锚定后把 9.0pt 内的续行并进来——阈值卡在续行间距与最小真实行距之间。
- 图片表则按像素列带取数(x 区间)再按 y 聚行,与 PDF 走同一套行规则;个别格识别存疑时多倍率重 OCR,并用「外语 = 语数外 − 数学 − 语文 ∈ [0,150]」这条恒等式反推唯一解(宝山一处「数学 90 被认成 06」就是这样抓出来的)。
这些错法肉眼在成品页面上都看不出来,只能靠几何校验与算术约束发现。
三、质检:多层校验 + 独立对拍
- 数值自洽:每条记录校验 ①总分在合理区间;②「语数外 ≥ 数学 + 语文」;③统招表另加「录取分 − 语数外 − 综合测试 ∈ [0,150]」(=道法+历史+体育三科满分);④「是否同分优待」列取值只能是「是/否」。
- 覆盖完整性:每一行都要求有对应高中名;确实缺失的逐条列出,不静默丢弃。
- 与独立真值对拍:贯通类表格以「官方 PDF 里出现的 6 位招生代码去重数量」为真值,名额分配表以官方表格行数为真值;统招表以「文件中出现的全部 6 位招生代码是否都被解析到」为真值,逐区打印「文中代码 N ✓全中 / ⚠缺 […]」。
- 八道闸门 + 独立复算(统招):分数域、分科值域、全部行有代码、全部行已归一化、同码跨区极差 > 0、各区概览、跨区投放码单列——全部通过;另由一段与解析器完全无关的脚本从成品 JSON 重算全部关键统计量(行数、逐区中位、分数段分布、跨区极差、低于投档线条数),与页面上的数字逐项对齐。
- 构建期十八道闸门:本站是数据驱动的静态站,每次构建都会自动跑十八道检查(必备元素、死链、sitemap、外链资源、图表配平、数据源一致性、分片完整性、图表与数据同源、倒推内核可算、客户端脚本真跑一遍等)。任何一道不过,构建直接失败、不会发版。这意味着「页面上写的数字」与「用户实际查到的数据」不可能分叉。
当前状态:名额分配 18,384 条记录、统招 963 条记录,核心数值字段校验通过率 100%;因官方版式换行导致的校名截断已降至个位数百分比,且不影响按片段检索。
四、三件我们明确不做的事
① 不估算各区报名人数。官方不公布该口径。市面上「浦东 3.3 万」这类数字多为机构推算,来源与方法不透明,本站不采用、不转述。
② 不预测录取概率。录取结果取决于当年报考人数与志愿分布,这两项在录取结束前都不可知。任何「你考 X 分有 Y% 概率」都是无依据的。
③ 不给学校排名。「录取最低分」是末位被录取者的成绩,受志愿填报行为影响,把它当作学校水平的排序依据是误用。
五、你可以怎么用这些数据
- 定位:查清楚某所初中在某年、通过「名额分配到校」进入某所高中需要的分数水平,以及对应各科分数结构;
- 对比结构:看同一高中在不同区的到区门槛差异,理解「区内竞争」的实际含义;
- 观察单科:总分为末位成绩时,单科分布能反映同分情况下的差异来源。
请把这些数据当作理解规则与结构的材料,而不是「明年会考多少分」的预测依据。