news 2026/9/2 16:58:20

【实战】Python爬取全国考研数据与可视化(附源码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【实战】Python爬取全国考研数据与可视化(附源码

很多考研同学都会关注院校招生人数、专业分数线等信息,手动整理效率极低。本篇用 Python 完成接口爬虫 + 数据存储 + 可视化分析全流程实战,从考研网站抓取院校专业数据,再通过图表直观分析数据,适合爬虫、数据分析入门学习。

## 一、项目的环境准备

pip install requests pandas pyecharts


## 二、整体开发思路

整个项目分为两大模块:数据爬虫和数据可视化。

1. 爬虫模块:通过三层接口请求,依次获取院校、专业、分数线,最终保存为 CSV 文件;
2. 可视化模块:读取 CSV 数据、清洗数据,使用 `pyecharts` 绘制多类图表并整合展示。

## 第一部分:数据爬取(分段拆解)


爬虫采用三层 POST 接口请求,下面按功能拆分模块,逐个讲解。



### 1.1 导入依赖 + 初始化全局变量


先导入需要的库,并定义列表用来存放最终爬取的数据。

```python import random import requests import time import pandas # 定义一个空列表,装整体数据 add_data = [] ```

### 1.2 配置请求头(模拟浏览器,防拦截)

网站接口会校验访问来源,配置请求头伪装成正常浏览器访问。

```python # 请求头 h = { "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` 代表北京市。

```python # 院校接口地址与请求参数 school_api = "https://api.kaoyan.cn/pc/school/schoolList" school_params = { "page": 1, "limit": 20, "province_id": "11", "type": "", "feature": "", "school_name": "" } # 发送POST请求,解析返回的JSON数据 res_school = requests.post(school_api, headers=headers, data=school_params) school_list = res_school.json()["data"]["data"] ```

### 1.4 遍历院校 + 第二层请求:获取院校专业信息


循环每一所学校,根据学校 ID 调用接口,获取该学校下所有专业信息。

```python # 遍历每一所学校 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 查询分数线,增加异常捕获处理无分数的情况。承接上一段循环,在内部继续编写:

```python # 遍历当前学校下的所有专业 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 数据组装 + 延时防反爬


把单条数据整理成字典存入列表,设置随机休眠,降低请求频率。

```python # 组装单条完整数据 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` 导出为本地表格文件。

```python # 转为DataFrame并保存CSV df = pd.DataFrame(all_data) df.to_csv("mnt/北京考研数据.csv", index=False, encoding="utf-8-sig") print("数据爬取完成,已保存!") ``` ## 爬虫的完整代码 ```python import random import requests import time import pandas # 定义请求网址:发送3次请求 # 定义一个空列表。来装每个学校的专业数据 alldata = [] # 1.第一个请求网址 以北京城市的大学数据为例子 xxurl = "https://api.kaoyan.cn/pc/school/schoolList" # 请求头 h = { "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/" } # 传递的信息 xxdata = { "page": 1, "limit": 20, "province_id": "11", "type": "", "feature": "", "school_name": "" } # 发请求 post请求单独传递的参数,复制给data xxres = requests.post(url=xxurl, headers=h, data=xxdata) xxlist = xxres.json()["data"]["data"] # 直接调用json() ''' 需要从学校信息里面获取每个学校名称和唯一id值, ''' for xx in xxlist: xname = xx["school_name"] print(f"开始爬取{xname}的专业") xid = xx["school_id"] # 携带这个id向该学校的详情页发请求,获取对应学校专业 zyurl = "https://api.kaoyan.cn/pc/school/planListV2" # 专业需要携带的请求,发请求 zydata = {"limit": 5, "school_id": xid} # 带上每个学校不同的id号,获取对应的专业 # 获取专业发请求 第二次请求: zyres = requests.post(url=zyurl, headers=h, data=zydata) ''' 需要从专业信息里面提取什么,和下一步关联是什么? 专业,招生数,学习方式,院系 ''' zylist = zyres.json()["data"]["data"] # 循环每一个专业,提取四个内容,找分数线的关键数据 for zy in zylist: zyname = zy["special_name"] num = zy["recruit_number"] stype = zy["recruit_type_name"] departName = zy["depart_name"] # 从当前每个专业数据里面获取对应专业唯一的id,携带id发送第三次请求,拿到每个专业的分数线 pid = zy["plan_id"] ''' 准备发第三次请求,携带专业id,拿分数线 ''' fsurl = "https://api.kaoyan.cn/pc/school/planDetailV2" # 准备传递的数据 fsdata = {"is_apply": 2, "plan_id": pid} # 发请求 fsres = requests.post(url=fsurl, headers=h, data=fsdata) # 获取专业分数。某些专业没有分数数据,找不到会报错,程序崩溃。try捕获报错 try: score = fsres.json()["data"]["min_score"] except Exception as e: print(f"该专业暂无分数{e}") score = "暂无" # 整合数据,6个内容 info = { "学校": xname, "专业": zyname, "院系": departName, "招生数": num, "学习方式": stype, "分数线": score, } alldata.append(info) # 加一个等待时间,避免短时间爬取太快 time.sleep(random.randint(1, 3)) # 存为本地的csv数据 用pandas转数据为表格数据类型 df = pandas.DataFrame(alldata) df.to_csv("北京考研数据.csv", index=False) print("保存成功") ```


## 第二部分:数据可视化



读取上面生成的 CSV 数据,做数据清洗 + 多图表绘制,依旧按功能拆分代码。


### 2.1 导入可视化相关库

```python import pandas as pd from pyecharts.charts import Page, Bar, Line from pyecharts import options as opts ```


### 2.2 读取数据 + 数据清洗



过滤无效数据,筛选出 985 院校、全日制的有效数据,并转换字段类型。

```python # 读取本地CSV文件 df = pd.read_csv("mnt/全国考研数据.csv") # 数据清洗:过滤无效值、筛选目标数据 df_clean = df[df["招生人数"] != "--"] df_clean = df_clean[df_clean["学习方式"] == "全日制"] df_clean = df_clean[df_clean["985"] == "是"] df_clean["招生人数"] = df_clean["招生人数"].astype(int) ```


### 2.3 模块一:绘制专业招生人数 TOP15 柱状图



统计热门专业招生总数,取前 15 名绘制柱状图,并优化 X 轴文字展示。

```python # 分组统计各专业招生总人数,倒序取前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 模块二:筛选金融专业数据



单独提取金融专业数据,按学校分组,统计招生数与最低分数线。

```python # 筛选金融专业所有数据 df_finance = df_clean[df_clean["专业"] == "金融"] # 按学校分组,聚合计算招生总数、最低分数线 finance_school = df_finance.groupby("学校名称").agg( 总招生数=("招生人数", "sum"), 最低分数=("分数线", "min") ).reset_index().sort_values("最低分数").head(10) ```


### 2.5 模块三:绘制分数线折线图


基于上一步处理好的数据,绘制院校分数线折线图。

```python # 创建折线图 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 模块四:绘制招生人数柱状图 + 图表叠加

绘制金融专业招生人数柱状图,并将折线图叠加在柱状图上,实现组合图表。

```python # 创建柱状图(招生人数) 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` 容器把多张图表放在同一个页面,最终渲染出网页文件。

```python # 创建页面容器 page = Page(layout=Page.SimplePageLayout) # 加入所有图表 page.add(bar1, bar2) # 渲染为本地HTML,浏览器可直接打开 page.render("考研数据可视化.html") print("图表绘制完成!") ```



## 完整数据可视化代码

```python # 从绘图的库中引入柱状图和折线图 from pyecharts.charts import Page, Bar, Line # 需要安装:pip install pyecharts from pyecharts import options as opts import pandas # 读取、清除脏数据、过滤需要的数据 dfData = pandas.read_csv("全国考研数据.csv") # 招生人数清洗 newdf = dfData[dfData["招生人数"] != "--"] newdf = newdf[newdf["学习方式"] == "全日制"] newdf = newdf[newdf["985"] == "是"] # 转换类型 newdf["招生人数"] = newdf["招生人数"].astype(int) # 分组统计不同专业的招生数,从高到低取前15个 zydata = newdf.groupby("专业")["招生人数"].sum().sort_values( ascending=False ).head(15).reset_index(name="招生总人数") # 创建柱状图 bar = Bar() x_data = zydata["专业"].tolist() y_data = zydata["招生总人数"].tolist() bar.add_xaxis(x_data) bar.add_yaxis("各专业招生总数", y_data, color="red") # 优化X轴,让名称完整显示并倾斜30度 bar.set_global_opts( xaxis_opts=opts.AxisOpts( axislabel_opts={ "interval": 0, "rotate": 30 } ) ) # 查看金融专业的招生情况 major = newdf[newdf["专业"] == "金融"] # 按学校聚合招生总数与最低分数 majorschool = major.groupby("学校名称").agg( 总招生数=("招生人数", "sum"), 最低分数=("分数线", "min") ).reset_index() # 按最低分数排序,取前10个 resultdata = majorschool.sort_values( by="最低分数", ascending=True ).head(10) # 绘制各学校金融专业分数线对比折线图 x_data = resultdata["学校名称"].tolist() y_data = resultdata["最低分数"].tolist() line = Line() line.add_xaxis(x_data) line.add_yaxis( "各学校金融专业分数线", y_data, z_level=2, linestyle_opts=opts.LineStyleOpts(color="#E53E3E") ) line.set_global_opts( xaxis_opts=opts.AxisOpts( axislabel_opts={ "interval": 0, "rotate": 30 } ) ) # 绘制各学校金融专业招生人数对比柱状图 y2_data = resultdata["总招生数"].tolist() bar2 = Bar() bar2.add_xaxis(x_data) bar2.add_yaxis("金融专业招生总数对比", y2_data, 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(bar, line, bar2) # 渲染合成的图表 page.render("考研数据可视化.html") print("绘制结束") ```


##三、总结


1. 爬虫部分:采用接口直爬方式,分三层请求数据,搭配请求头、延时、异常捕获应对基础反爬,最终落地为 CSV 表格;

2. 可视化部分:先清洗脏数据,再分别绘制柱状图、折线图,支持图表叠加与多图合并,可视化结果直观易懂;

3. 拓展方向:修改省份 ID 爬取全国数据、增加分页逻辑、新增饼图 / 散点图丰富分析维度。



本期内容就分享到这里啦!如果你在实践过程中遇到任何问题,或者想获取文中提到的完整源码、
配置文件模板,以及更多进阶玩法,可扫码领取(无套路,纯分享)。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 16:57:43

基准测试内测实战指南:从环境搭建到结果分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/2 16:54:58

FPGA实战:BPSK调制解调与卷积编解码完整链路设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/2 16:51:38

网易版《我的世界》函数指令全解析:从零构建自动化系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/2 16:47:02

短视频运营与合规实操指南:从版权自查到数据复盘

之前在业务迭代里帮朋友打理短视频账号时,经常看到一类困惑:视频明明花了很多心思剪辑,画面、节奏、配音都调到位了,标题里也写了“高质量作品”“求推荐”,结果播放量还是不高,甚至偶尔收到“内容不适合继…

作者头像 李华
网站建设 2026/9/2 16:44:53

Claude Code 2.0 提示词优化:从指令驱动到意图理解的高效编程协作

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/2 16:43:37

AI帮你写代码的时候,顺手把53个后门包名也写进去了

开发者向模型询问, 帮其写一个REST API示例, 模型生成了一段看上去颇完整的代码, 有, 有pip rest -, 甚至还有注释对每个依赖的用途予以说明, 开发者进行复制粘贴, 于终端运行了一遍pip, 一切正常, 只是存在一个细节。rest-这个包名不存在。真实安装包是。假设这仅仅是一回运行…

作者头像 李华