2025 年全国高考投档线数据批量爬取实战:从 31 省教育考试院提取原始 PDF/Excel

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特点
直接 PDFhttps://www.bjeea.cn/uploads/soft/250719/178-250G9223234.pdf最友好,直接下载
带参数 PDFhttps://www.cqksy.cn/.../xxx.pdf?fileName=...&fileSize=...扩展名提取易出错
直接 XLShttps://www.zjzs.net/attach/0/xxxx/xxxx.xls旧版 Excel 格式
直接 XLSXhttp://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 省)

省份格式批次覆盖文件数
广东PDF历史/物理/体育/美术/音乐/舞蹈/书法/播音/表导演9
北京PDF提前批 A/B 段 + 普通批4
江苏PDF/XLS/XLSX提前批 7 批次 + 普通批15
山东XLS常规批/艺术类/体育类/春季高考8
河北XLSX提前批 B 段 + 普通批4
浙江XLS普通类第一段/第二段 + 体育类3
江西PDF历史物理/体育/三校生3
湖南XLSX体育类 + 普通批物理2
辽宁XLSX历史/物理类2
贵州PDF历史/物理类2
上海PDF本科普通批1
重庆PDF本科普通批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)  ← 最大的文件

七、踩坑记录

  1. 广东附件编号重复eea.gd.gov.cn 的 9 个附件 PDF 文件名都是 4746781.pdf,只有 URL 路径中的 585885585893 编号不同,容易误判为重复文件。

  2. 江苏文件数量爆炸:江苏按"历史/物理 × 提前批/普通批 × 多次征集"拆分,一次产出 15 个文件,需做好文件命名区分。

  3. 贵州 PDF 体积巨大:贵州物理类 PDF 达 6.8 MB,原因是该省包含所有院校专业组的完整投档数据,包含大量表格。

  4. 黑龙江 .xlsx 链接返回 HTML:直接请求 .xlsx 链接时,服务器会返回 3.8 KB 的 HTML 页面(可能是登录态校验或防盗链),最终放弃该省部分批次。

  5. 文件命名中的乱码:由于 eol.cn 页面编码为 GBK/GB2312,BeautifulSoup 解析后的中文文件名可能乱码,建议用 Unicode 规范化处理。


八、总结与建议

这套流程的复用价值

如果你需要爬取 2026 年及以后的投档线数据,只需做以下调整:

  1. 修改 URL 时间路径:将 202507 改为 202607
  2. 更新各省页面编号:eol.cn 的页面编号每年变化,需重新提取
  3. 关注政策变化:预计更多省份会跟进"仅考生可查"模式,可下载的省份可能逐年减少

对开发者的建议

  • 不要硬编码 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 表格)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云樱梦海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值