2025 年全国高考投档线数据批量爬取实战:从 31 省教育考试院提取原始 PDF/Excel
项目背景:2025 年是全国新高考改革的落地之年,各省投档线数据格式、公开政策差异巨大。本文记录了一次完整的批量爬取过程,最终成功下载 64 份原始投档线文件,覆盖 12 个省份,并总结出一套可复制的工作流。
附:爬取技能下载链接:https://pan.quark.cn/s/03d7ee1a08f8
一、为什么需要批量爬取?
每年 7-8 月,全国 31 个省级教育考试院会陆续发布当年高考各批次的投档线/录取数据。这些数据对于以下场景至关重要:
- 高考志愿填报工具开发:需要结构化历史数据做推荐算法
- 教育数据分析:研究各省录取趋势、热门专业冷热变化
- 自媒体/升学规划:整理各省分数线对比文章
但问题是:数据极其分散。每个省有自己的考试院官网、不同的文件格式(PDF/XLS/XLSX)、不同的发布方式(直接下载/在线查询/仅考生可查)。手动逐个下载耗时巨大,且容易遗漏。
二、数据源分析:从"入口页"到"各省详情页"
2.1 核心入口:中国教育在线
经过多次搜索验证,发现中国教育在线(eol.cn)每年会汇总各省投档线发布页面,是最佳的爬取入口:
https://www.eol.cn/e_html/gk/gktoudang/index.shtml
这个页面包含 31 个省份的导航锚点,以及每个省对应的详情页链接,格式高度规律:
https://gaokao.eol.cn/{省份拼音}/dongtai/202507/t202507{日}_{编号}.shtml
提取方法:
# 一步提取所有 2025 年 7 月的省份详情页链接
curl -s "https://www.eol.cn/e_html/gk/gktoudang/index.shtml" -o /tmp/eol.html
grep -oP 'href="https://gaokao.eol.cn/[^"]+/dongtai/202507/t202507[^"]+\.shtml"' /tmp/eol.html \
| sed 's/href="//;s/"$//' | sort -u > eol_links.txt
实测一次提取到 100 条 2025 年 7 月各省投档线相关页面。
2.2 各省附件链接的 5 种形态
进入每个省份的详情页后,需要解析出指向原始数据文件的 <a> 标签。各省链接形态差异很大,总结如下:
| 类型 | 示例 URL | 特点 |
|---|---|---|
| 直接 PDF | https://www.bjeea.cn/uploads/soft/250719/178-250G9223234.pdf | 最友好,直接下载 |
| 带参数 PDF | https://www.cqksy.cn/.../xxx.pdf?fileName=...&fileSize=... | 扩展名提取易出错 |
| 直接 XLS | https://www.zjzs.net/attach/0/xxxx/xxxx.xls | 旧版 Excel 格式 |
| 直接 XLSX | http://file.hebeea.edu.cn/files/article/2025/07/xxxx.xlsx | 新版 Excel 格式 |
| 下载系统跳转 | https://www.hljea.org.cn/system/_content/download.jsp?... | 需要 session,常失败 |
三、技术难点与解决方案
难点 1:URL 参数污染导致扩展名提取错误
重庆的链接如下:
https://www.cqksy.cn/.../xxx.pdf?fileName=2025xxb.pdf&fileSize=1033364
如果直接用 url.split('.')[-1],会得到 pdf&fileName=2025xxb,脚本判断不是标准扩展名,会 fallback 成 .bin。
解决方案:用正则先截断查询参数:
# 安全提取扩展名
ext = url.split('.')[-1].split('?')[0].split('&')[0][:4].lower()
if ext not in ['pdf', 'xls', 'xlsx']:
ext = 'bin' # 先存为 bin,后续用文件头修复
难点 2:SSL 握手失败
湖南的 hneeb.cn 域名使用较老的 SSL 配置,直接用 requests.get() 会报错:
SSLError: SSLV3_ALERT_HANDSHAKE_FAILURE
解决方案:
import urllib3
urllib3.disable_warnings()
# 关闭 SSL 验证
resp = requests.get(url, headers=headers, verify=False, timeout=30)
或者将 https:// 改为 http://(如果服务器支持)。
难点 3:下载到 HTML 伪装页
黑龙江的 download.jsp 链接,如果直接请求,返回的不是 Excel,而是** HTML 登录页面**(约 3.8 KB)。
解决方案:双重验证——HTTP 状态码 + 文件头 + 文件大小:
# 验证不是 HTML 伪装
is_html = b'<html' in resp.content[:200].lower()
# 验证文件大小(真实 PDF/Excel 通常 > 10KB)
if resp.status_code == 200 and not is_html and len(resp.content) > 1000:
with open(filepath, 'wb') as f:
f.write(resp.content)
难点 4:.bin 文件扩展名修复
由于上述参数污染问题,下载后会残留 .bin 文件。必须根据**文件头魔数(Magic Number)**修复:
def get_true_extension(filepath):
with open(filepath, 'rb') as f:
header = f.read(20)
if header[:4] == b'%PDF':
return 'pdf'
elif header[:4] == b'\xd0\xcf\x11\xe0': # OLE2 旧版 Excel
return 'xls'
elif b'PK\x03\x04' in header[:10]: # ZIP 压缩包 = 新版 XLSX
return 'xlsx'
else:
return None
# 扫描并重命名
for f in os.listdir(pdf_dir):
if f.endswith('.bin'):
ext = get_true_extension(os.path.join(pdf_dir, f))
if ext:
new_name = f.replace('.bin', f'.{ext}')
os.rename(old_path, new_path)
四、核心代码:从 URL 发现到文件落地
阶段 1:批量提取各省下载链接
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept-Language': 'zh-CN,zh;q=0.9',
}
province_map = {
'an_hui': '安徽', 'bei_jing': '北京', 'chong_qing': '重庆',
'guang_dong': '广东', 'jiang_su': '江苏', 'he_bei': '河北',
# ... 其他省份映射
}
results = {}
for url in province_page_links: # 从 eol.cn 提取的 100 个页面
try:
parts = urlparse(url).path.split('/')
prov_key = parts[2]
province = province_map.get(prov_key, prov_key)
resp = requests.get(url, headers=headers, timeout=20)
resp.encoding = resp.apparent_encoding
soup = BeautifulSoup(resp.text, 'html.parser')
downloads = []
for a in soup.find_all('a', href=True):
href = a['href']
full = urljoin(url, href)
lower = full.lower()
# 捕获直接文件链接 + 附件/下载链接
if any(ext in lower for ext in ['.pdf', '.xls', '.xlsx']):
downloads.append({'url': full, 'text': a.get_text(strip=True)})
elif 'attachment' in lower or 'download' in lower:
downloads.append({'url': full, 'text': a.get_text(strip=True)})
if downloads:
results[province] = {'page_url': url, 'downloads': downloads}
time.sleep(0.8) # 防限流
except Exception as e:
print(f"Error {url}: {e}")
阶段 2:批量下载 + 实时验证
import urllib3
urllib3.disable_warnings()
for prov, info in results.items():
for dl in info['downloads']:
url = dl['url']
# 安全提取扩展名
ext = url.split('.')[-1].split('?')[0].split('&')[0][:4].lower()
if ext not in ['pdf', 'xls', 'xlsx']:
ext = 'bin'
filename = f"{prov}_2025_{dl['text'][:20]}.{ext}"
filepath = os.path.join(output_dir, filename)
# 跳过已存在的有效文件
if os.path.exists(filepath) and os.path.getsize(filepath) > 1000:
continue
resp = requests.get(url, headers=headers, timeout=30, verify=False)
content_type = resp.headers.get('Content-Type', '').lower()
is_html = b'<html' in resp.content[:200].lower() or 'text/html' in content_type
if resp.status_code == 200 and not is_html and len(resp.content) > 1000:
with open(filepath, 'wb') as f:
f.write(resp.content)
print(f"✅ Downloaded: {filename} ({len(resp.content)} bytes)")
else:
print(f"❌ Failed: {filename} (html={is_html}, size={len(resp.content)})")
五、各省数据公开政策差异(核心干货)
经过实际爬取验证,2025 年各省投档线数据的公开政策可分为 3 类:
类型 A:完全公开,可直接下载(12 省)
| 省份 | 格式 | 批次覆盖 | 文件数 |
|---|---|---|---|
| 广东 | 历史/物理/体育/美术/音乐/舞蹈/书法/播音/表导演 | 9 | |
| 北京 | 提前批 A/B 段 + 普通批 | 4 | |
| 江苏 | PDF/XLS/XLSX | 提前批 7 批次 + 普通批 | 15 |
| 山东 | XLS | 常规批/艺术类/体育类/春季高考 | 8 |
| 河北 | XLSX | 提前批 B 段 + 普通批 | 4 |
| 浙江 | XLS | 普通类第一段/第二段 + 体育类 | 3 |
| 江西 | 历史物理/体育/三校生 | 3 | |
| 湖南 | XLSX | 体育类 + 普通批物理 | 2 |
| 辽宁 | XLSX | 历史/物理类 | 2 |
| 贵州 | 历史/物理类 | 2 | |
| 上海 | 本科普通批 | 1 | |
| 重庆 | 本科普通批 | 1 |
类型 B:仅在线查询/网页展示,无静态文件(9 省)
| 省份 | 公开方式 | 能否批量爬取 |
|---|---|---|
| 安徽 | 网页长图片 | ❌ 无法提取结构化数据 |
| 广西 | 官方网页表格 | ❌ 需解析 HTML 表格 |
| 海南 | 官方网页表格 | ❌ 需解析 HTML 表格 |
| 福建 | 数字服务大厅在线查询 | ❌ 无静态文件链接 |
| 天津 | 系统查询 | ❌ 无公开文件 |
| 内蒙古 | 考生服务平台查询 | ❌ 无公开文件 |
| 吉林 | 考生服务平台查询 | ❌ 无公开文件 |
| 陕西 | 个人查询渠道 | ❌ 无公开文件 |
| 西藏 | 未检索到完整文件 | ❌ 无数据 |
类型 C:仅考生本人可查(5 省)——2025 年政策收紧
⚠️ 这是 2025 年最大的变化:多个省份不再向社会公开完整投档线 PDF,仅支持考生登录系统查询自己填报志愿的院校投档结果。
| 省份 | 查询入口 | 备注 |
|---|---|---|
| 河南 | 河南省普通高校招生考生服务平台 | 2025 年起不公开完整投档线 |
| 四川 | 四川省教育考试院高考数字服务大厅 | 同上 |
| 云南 | 云南省普通高等学校招生考生服务平台 | 同上 |
| 甘肃 | 甘肃阳光高考信息平台 | 同上 |
| 青海 | 青海省普通高校招生考试考生综合信息平台 | 同上 |
类型 D:需特殊渠道获取(5 省)
| 省份 | 障碍 | 解决方案 |
|---|---|---|
| 湖北 | 需微信公众号/二维码扫描下载 | 无法自动化 |
| 黑龙江 | 部分批次需 session 登录下载系统 | 可寻找替代直接链接 |
| 宁夏 | PDF 链接常返回 404 | 需通过官方系统 |
| 新疆 | 以图片/网页形式发布 | 需 OCR 或手动整理 |
| 山西 | 需通过下载系统获取 | 无直接链接 |
六、成果统计
📊 最终交付
├── 有效文件总数:64 个
├── 覆盖省份:12 个(广东、北京、江苏、山东、河北、浙江、江西、湖南、辽宁、贵州、上海、重庆)
├── 文件格式分布:
│ ├── PDF:32 个
│ ├── XLS(旧版 Excel):24 个
│ └── XLSX(新版 Excel):8 个
├── 总数据量:约 25 MB
├── 无法下载省份:19 个(政策限制或技术障碍)
└── 输出目录:{workspace}/output/2025_toudang/pdf/
文件命名示例:
广东_2025_本科普通类历史.pdf (421 KB)
江苏_2025_本科普通批历史PDF.pdf (652 KB)
山东_2025_普通类常规批第1次.xls (1.9 MB)
河北_2025_本科普通批物理.xlsx (1.3 MB)
浙江_2025_普通类第一段.xls (2.0 MB)
贵州_2025_本科物理类.pdf (6.8 MB) ← 最大的文件
七、踩坑记录
-
广东附件编号重复:
eea.gd.gov.cn的 9 个附件 PDF 文件名都是4746781.pdf,只有 URL 路径中的585885~585893编号不同,容易误判为重复文件。 -
江苏文件数量爆炸:江苏按"历史/物理 × 提前批/普通批 × 多次征集"拆分,一次产出 15 个文件,需做好文件命名区分。
-
贵州 PDF 体积巨大:贵州物理类 PDF 达 6.8 MB,原因是该省包含所有院校专业组的完整投档数据,包含大量表格。
-
黑龙江
.xlsx链接返回 HTML:直接请求.xlsx链接时,服务器会返回 3.8 KB 的 HTML 页面(可能是登录态校验或防盗链),最终放弃该省部分批次。 -
文件命名中的乱码:由于 eol.cn 页面编码为 GBK/GB2312,BeautifulSoup 解析后的中文文件名可能乱码,建议用 Unicode 规范化处理。
八、总结与建议
这套流程的复用价值
如果你需要爬取 2026 年及以后的投档线数据,只需做以下调整:
- 修改 URL 时间路径:将
202507改为202607 - 更新各省页面编号:eol.cn 的页面编号每年变化,需重新提取
- 关注政策变化:预计更多省份会跟进"仅考生可查"模式,可下载的省份可能逐年减少
对开发者的建议
- 不要硬编码 URL:各省考试院每年会改版,建议每年重新跑一遍 URL 发现流程
- 建立文件头验证机制:无论什么扩展名,下载后必须验证 Magic Number
- 做好省份政策白名单:建议维护一个"可下载省份清单",每年更新
- 考虑购买商业数据:如果项目需要全部 31 省数据,咕咕数据等商业 API 提供结构化接口(需付费),比自己爬取更稳定
文件已存档
本项目生成的 64 份原始文件 + 下载清单 已永久保存,同时输出了一份 SKILL_batch_download_gaokao_toudang.md 作为标准化操作手册,供后续快速复用。
附:下载链接:https://pan.quark.cn/s/03d7ee1a08f8
数据年份:2025 年全国高考投档线
代码环境:Python 3.12 + requests + BeautifulSoup4
如果对你有帮助,欢迎点赞收藏!如有问题可在评论区交流,或关注后续数据解析文章(将 PDF/XLS 提取为结构化 Excel 表格)。
1407

被折叠的 条评论
为什么被折叠?



