[实战]Python爬取全国考研数据与可视化(附源码)
很多考研同学都会关注院校招生人数、专业分数线等信息,手动整理效率极低。本篇用 Python 完成接 口爬虫 + 数据存储 + 可视化分析全流程实战,从考研网站抓取院校专业数据,再通过图表直观分析数据,适合爬虫、数据分析入门学习。
一、项目的环境准备
pip install requests pandas pyecharts二、整体开发思路
整体项目分分为两大模块:数据爬虫和数据可视化。
- 数据爬虫: 通过三层接口请求,依次获取院校、专业、分数线,最终保存为CSV文件;
- 数据可视化: 读取CSV数据, 清洗数据, 使用pyecharts绘制多类图表并整合展示。
第一部分: 数据爬虫(分段拆解)
爬虫采用的是三层 POST 接口请求,下面按功能拆分, 逐个讲解。
1.1 导入依赖 + 初始化全局变量
先导入需要的库, 并定义列表用于存放最终爬取到的数据。
import random import requests import time import pandas # 定义请求网址:发送3次请求 # 定义一个空列表。来装每个学校的专业数据 alldata = []1.2 配置请求头(模拟浏览器, 防止拦截)
网络接口会校验访问的来源, 配置请求头伪装成正常的浏览器访问。
# 请求头 headers = { "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36", "referer":"https://www.kaoyan.cn/" }1.3 第一层请求: 获取北京区域院校列表
调用接口拿到院校名称、学校唯一 ID, province_id=11 代表北京市。
# 1.第一个请求网址 以北京城市的大学数据为例子 school_api = "https://api.kaoyan.cn/pc/school/schoolList" # 传递的信息 school_params = { "page":1, "limit":20, "province_id":"11", "type":"", "feature":"", "school_name":"" } # 发请求 post请求单独传递的参数,复制给data res_school= requests.post(url=school_api,headers=headers,data=school_params) school_list = res_school.json()["data"]["data"] # 直接调用json()1.4 遍历院校 + 第二层请求:获取院校专业信息
循环每一所学校,根据学校 ID调用接口,获取该学校下所有专业信息。
# 遍历每一所学校 for school in school_list: school_name = school["school_name"] school_id = school["school_id"] print(f"正在爬取:{school_name}") # 专业接口 + 请求参数 major_api = "https://api.kaoyan.cn/pc/school/planListV2" major_params = {"limit": 5, "school_id": school_id} # 请求专业数据 res_major = requests.post(major_api, headers=headers, data=major_params) major_list = res_major.json()["data"]["data"]1.5 遍历专业 + 第三层请求: 查询专业分数线
循环每个专业,根据专业 ID查询分数线,增加异常捕获处理无分数的情况。 承接上一段循环,在内部继续编写:
# 遍历当前学校下的所有专业 for major in major_list: major_name = major["special_name"] recruit_num = major["recruit_number"] study_type = major["recruit_type_name"] depart = major["depart_name"] plan_id = major["plan_id"] # 分数线接口 + 参数 score_api = "https://api.kaoyan.cn/pc/school/planDetailV2" score_params = {"is_apply": 2, "plan_id": plan_id} # 请求分数线数据 res_score = requests.post(score_api, headers=headers, data=score_params) # 异常捕获:部分专业暂无分数线,避免程序报错崩溃 try: min_score = res_score.json()["data"]["min_score"] except: min_score = "暂无"1.6 整合数据 + 延时爬取防反爬
把单条数据整理成字典存入列表,设置随机休眠,降低请求频率。
# 组装单条完整数据 row = { "学校": school_name, "专业": major_name, "院系": depart, "招生数": recruit_num, "学习方式": study_type, "分数线": min_score } all_data.append(row) # 随机延时 1~3 秒,简单规避反爬 time.sleep(random.randint(1, 3))1.7 数据本地持久化:保存为 CSV 文件
所有数据爬取完成后,使用 pandas 导出为本地表格文件。
# 转为DataFrame并保存CSV df = pandas.DataFrame(all_data) df.to_csv("mnt/北京考研数据.csv", index=False, encoding="utf-8-sig") print("数据爬取完成,已保存!")数据爬虫的完整代码
import random import requests import time import pandas # 定义请求网址:发送3次请求 # 定义一个空列表。来装每个学校的专业数据 alldata = [] # 请求头 headers = { "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36", "referer":"https://www.kaoyan.cn/" } # 1.第一个请求网址 以北京城市的大学数据为例子 school_api = "https://api.kaoyan.cn/pc/school/schoolList" # 传递的信息 school_params = { "page":1, "limit":20, "province_id":"11", "type":"", "feature":"", "school_name":"" } # 发请求 post请求单独传递的参数,复制给data res_school= requests.post(url=school_api,headers=headers,data=school_params) school_list = res_school.json()["data"]["data"] # 直接调用json() # 遍历每一所学校 for school in school_list: school_name = school["school_name"] school_id = school["school_id"] print(f"正在爬取:{school_name}") # 专业接口 + 请求参数 major_api = "https://api.kaoyan.cn/pc/school/planListV2" major_params = {"limit": 5, "school_id": school_id} # 请求专业数据 res_major = requests.post(major_api, headers=headers, data=major_params) major_list = res_major.json()["data"]["data"] # 遍历当前学校下的所有专业 for major in major_list: major_name = major["special_name"] recruit_num = major["recruit_number"] study_type = major["recruit_type_name"] depart = major["depart_name"] plan_id = major["plan_id"] # 分数线接口 + 参数 score_api = "https://api.kaoyan.cn/pc/school/planDetailV2" score_params = {"is_apply": 2, "plan_id": plan_id} # 请求分数线数据 res_score = requests.post(score_api, headers=headers, data=score_params) # 异常捕获:部分专业暂无分数线,避免程序报错崩溃 try: min_score = res_score.json()["data"]["min_score"] except: min_score = "暂无" # 组装单条完整数据 row = { "学校": school_name, "专业": major_name, "院系": depart, "招生数": recruit_num, "学习方式": study_type, "分数线": min_score } all_data.append(row) # 随机延时 1~3 秒,简单规避反爬 time.sleep(random.randint(1, 3)) # 转为DataFrame并保存CSV df = pandas.DataFrame(all_data) df.to_csv("mnt/北京考研数据.csv", index=False, encoding="utf-8-sig") print("数据爬取完成,已保存!")第二部分:数据可视化
读取上面生成的 CSV 数据,做数据清洗 + 多图表绘制,依旧按功能拆分代码
2.1 导入可视化相关库
import pandas as pd from pyecharts.charts import Page, Bar, Line from pyecharts import options as opts2.2 读取数据 + 数据清洗
过滤无效数据,筛选出985 院校、全日制的有效数据,并转换字段类型
# 读取本地CSV文件 df = pd.read_csv("mnt/全国考研数据.csv") # 数据清洗:过滤无效值、筛选目标数据 df_clean = df[df["招生人数"] != "--"] # 剔除招生人数为空的数据 df_clean = df_clean[df_clean["学习方式"] == "全日制"] # 只保留全日制 df_clean = df_clean[df_clean["985"] == "是"] # 只保留985院校 df_clean["招生人数"] = df_clean["招生人数"].astype(int) # 转为数值类型2.3 模块一:绘制专业招生人数 TOP15 柱状图
统计热门专业招生总数,取前 15 名绘制柱状图,并优化 X 轴文字展示。
# 分组统计各专业招生总人数,倒序取前15 top15_major = df_clean.groupby("专业")["招生人数"].sum()\ .sort_values(ascending=False).head(15)\ .reset_index(name="招生总人数") # 创建柱状图对象 bar1 = Bar() bar1.add_xaxis(top15_major["专业"].tolist()) bar1.add_yaxis("专业招生总人数", top15_major["招生总人数"].tolist(), color="red") # 配置X轴:文字倾斜、完整展示名称 bar1.set_global_opts( xaxis_opts=opts.AxisOpts(axislabel_opts={"interval": 0, "rotate": 30}) )2.4 模块二:筛选金融专业数据
单独提取金融专业数据,按学校分组,统计招生数与最低分数线。
# 筛选金融专业所有数据 df_finance = df_clean[df_clean["专业"] == "金融"] # 按学校分组,聚合计算招生总数、最低分数线 finance_school = df_finance.groupby("学校名称").agg( 总招生数=("招生人数", "sum"), 最低分数=("分数线", "min") ).reset_index().sort_values("最低分数").head(10)2.5 模块三:绘制分数线折线图
基于上一步处理好的数据,绘制院校分数线折线图。
# 创建折线图 line = Line() line.add_xaxis(finance_school["学校名称"].tolist()) line.add_yaxis( "最低分数线", finance_school["最低分数"].tolist(), linestyle_opts=opts.LineStyleOpts(color="#E53E3E") ) # X轴样式优化 line.set_global_opts( xaxis_opts=opts.AxisOpts(axislabel_opts={"interval": 0, "rotate": 30}) )2.6 模块四:绘制招生人数柱状图 + 图表叠加
绘制金融专业招生人数柱状图,并将折线图叠加在柱状图上,实现组合图表
# 创建柱状图(招生人数) bar2 = Bar() bar2.add_xaxis(finance_school["学校名称"].tolist()) bar2.add_yaxis("金融专业招生数", finance_school["总招生数"].tolist(), color="data") bar2.set_global_opts( xaxis_opts=opts.AxisOpts(axislabel_opts={"interval": 0, "rotate": 30}) ) # 折线图叠加到柱状图中 bar2.overlap(line)2.7 模块五:整合所有图表,生成 HTML 文件
使用 Page 容器把多张图表放在同一个页面,最终渲染出网页文件
# 创建页面容器 page = Page(layout=Page.SimplePageLayout) # 加入所有图表 page.add(bar1, bar2) # 渲染为本地HTML,浏览器可直接打开 page.render("考研数据可视化.html") print("图表绘制完成!")数据可视化的完整代码
import pandas as pd from pyecharts.charts import Page, Bar, Line from pyecharts import options as opts # 读取本地CSV文件 df = pd.read_csv("mnt/全国考研数据.csv") # 数据清洗:过滤无效值、筛选目标数据 df_clean = df[df["招生人数"] != "--"] # 剔除招生人数为空的数据 df_clean = df_clean[df_clean["学习方式"] == "全日制"] # 只保留全日制 df_clean = df_clean[df_clean["985"] == "是"] # 只保留985院校 df_clean["招生人数"] = df_clean["招生人数"].astype(int) # 转为数值类型 # 分组统计各专业招生总人数,倒序取前15 top15_major = df_clean.groupby("专业")["招生人数"].sum()\ .sort_values(ascending=False).head(15)\ .reset_index(name="招生总人数") # 创建柱状图对象 bar1 = Bar() bar1.add_xaxis(top15_major["专业"].tolist()) bar1.add_yaxis("专业招生总人数", top15_major["招生总人数"].tolist(), color="red") # 配置X轴:文字倾斜、完整展示名称 bar1.set_global_opts( xaxis_opts=opts.AxisOpts(axislabel_opts={"interval": 0, "rotate": 30}) ) # 筛选金融专业所有数据 df_finance = df_clean[df_clean["专业"] == "金融"] # 按学校分组,聚合计算招生总数、最低分数线 finance_school = df_finance.groupby("学校名称").agg( 总招生数=("招生人数", "sum"), 最低分数=("分数线", "min") ).reset_index().sort_values("最低分数").head(10) # 创建折线图 line = Line() line.add_xaxis(finance_school["学校名称"].tolist()) line.add_yaxis( "最低分数线", finance_school["最低分数"].tolist(), linestyle_opts=opts.LineStyleOpts(color="#E53E3E") ) # X轴样式优化 line.set_global_opts( xaxis_opts=opts.AxisOpts(axislabel_opts={"interval": 0, "rotate": 30}) ) # 创建柱状图(招生人数) bar2 = Bar() bar2.add_xaxis(finance_school["学校名称"].tolist()) bar2.add_yaxis("金融专业招生数", finance_school["总招生数"].tolist(), color="data") bar2.set_global_opts( xaxis_opts=opts.AxisOpts(axislabel_opts={"interval": 0, "rotate": 30}) ) # 折线图叠加到柱状图中 bar2.overlap(line) # 创建页面容器 page = Page(layout=Page.SimplePageLayout) # 加入所有图表 page.add(bar1, bar2) # 渲染为本地HTML,浏览器可直接打开 page.render("考研数据可视化.html") print("图表绘制完成!")三、项目总结
爬虫部分:采用接口直爬方式,分三层请求数据,搭配请求头、延时、异常捕获应对基础反爬,最终落 地为 CSV 表格。
可视化部分:先清洗脏数据,再分别绘制柱状图、折线图,支持图表叠加与多图合并,可视化结果直观 易懂。
三层接口对应关系:
| 步骤 | 接口 | 用途 | 入参 | 出参 |
| 第一层 | /pc/school/schoolList | 获取院校列表 | province_id | school_id |
| 第二层 | /pc/school/planListV2 | 获取专业列表 | school_id | plan_id |
| 第三层 | /pc/school/planDetailV2 | 获取专业分数线 | plan_id | min_score |
拓展方向: 修改省份ID爬取全国数据, 增加分页逻辑, 新增饼图 / 散点图丰富分析温度
本期内容就分享到这里啦!如果你在实践过程中遇到任何问题,或者想获取文中提到的完整源码、配置 文件模板,以及更多进阶玩法,可扫码领取:完整爬虫+可视化源码、2000+条北京考研数据(可直接用 于分析)、省份ID对照表(一键切换爬全国),无套路,纯分享。
