news 2026/9/8 11:28:52

用Python合并四大世界大学排名:数据清洗与实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Python合并四大世界大学排名:数据清洗与实战指南

简介:这份压缩包汇总了US News、软科、QS、THE四大全球大学排名截至2021年11月10日的数据,并附有配套Python脚本,面向需要做高校对比、留学选校或排名数据分析的研究者与学生。包内共有15个文件,包括8个py脚本、4个zip数据包和3个txt说明文件,整体约13.21MB。脚本覆盖各排名综合榜与学科榜的抓取和解析,利用requests、BeautifulSoup、pandas等工具可自动化下载与清洗数据;zip内为各机构原始排名表,txt则存放院校链接与名称等辅助信息。借助这些材料,使用者既能直接读取2022年最新排名,也能通过脚本二次开发,开展趋势分析和地区对比。该资源已有1182人学习,适合具备一定Python基础、希望获取结构化排名数据的学习者。 每年四大世界大学排名集中放榜的那几周,我手机基本都是炸的。学弟学妹问选校到底该看哪个榜单,客户问合同里的院校定位按什么口径算,连家里亲戚都要我解释同一所帝国理工在QS和软科里为什么差了几十位。问的人多了,我干脆把US News、软科、QS、THE 2022世界大学排名数据全部找齐,写一个python脚本统一清洗合并成一张表,之后谁再问,直接把表甩过去。这篇就把整个项目的完整思路讲清楚:数据从哪来、字段怎么对齐、学校名怎么统一、脚本怎么写,以及我实际跑数据时踩过的坑,适合所有准备跟排行榜数据打交道的人参考。

1. 四个榜单,四种游戏规则:合并之前先看清它们怎么排

1.1 各榜单的指标重心差异

做数据脚本的第一步不是写代码,而是先理解数据背后的规则。四个榜单看着都在给大学排名,实际上它们的考核口径差异很大,这决定了合并后的字段设计和分析逻辑。

榜单发布方核心指标倾向直观特点
US News全球大学排名《美国新闻与世界报道》全球学术声誉、论文引用、专著、国际合作医学与理工科学校占优,学校规模越大有优势
软科世界大学学术排名(ARWU)上海软科教育信息咨询诺奖/菲尔兹奖、高被引学者、Nature/Science论文、国际论文纯学术成果导向,不碰声誉问卷
QS世界大学排名Quacquarelli Symonds学术声誉、雇主声誉、师生比、国际教师/学生比例声誉类指标权重高,英语区学校天然吃香
THE世界大学排名泰晤士高等教育教学、研究、引用、国际展望、产业收入比QS更偏引用与科研产出,指标体系最细

一句话总结:软科看硬成果,US News和THE看科研影响,QS看声誉。同一个学校在不同榜单排名相差很大是正常现象,不是你眼睛出了问题。

1.2 “2022年”的版本陷阱

处理这些数据时第一个坑就是“2022年”本身有歧义。四个榜单的发布时间并不同步,QS在2022年6月发布的是QS World University Rankings 2023,THE在2022年10月发布的是THE World University Rankings 2023,US News在2022年10月发布的是2022-2023 Best Global Universities Rankings,只有软科在2022年8月发布的是ARWU 2022。如果你笼统地把它们称为“2022排名”,那么QS 2022和QS 2023其实相差整整一年。

我在脚本里把“2022”定义为榜单版本号或发布年份,而不是自然年。也就是说,如果用户传--year 2022,脚本默认匹配的是:软科2022、QS 2023、THE 2023、US News 2022-2023。这样既符合大多数人口中“2022年发布的世界大学排名”,又避免了版本混乱。你在复现时也可以按自己需要调整,参数化的好处就在这。

1.3 为什么非要合并成一张表

把四个榜单合并,不只是为了省去来回切换网页的麻烦。合并之后可以做很多单张表做不了的事:对比学校在不同体系里的位次差、计算排名极差识别稳定性、筛选出某一地区在四个榜单都进入前100的学校。这些分析对留学选校、合作评估、乃至自媒体内容创作都有实际价值。所以这个项目不是简单爬虫,而是一个典型的数据清洗与集成任务。

2. 数据从哪来:软科Excel、THE的CSV、QS的导出和US News的网页表格

2.1 软科:官方Excel是最省事的入口

软科官网每年会提供世界大学学术排名的完整数据下载,常见格式是Excel。这份表格里的字段一般是学校排名、学校名称、国家/地区、总分等,信息非常规整,个人研究用起来很舒服。用pandas读取时需要注意工作区,有些年份的表格不是第一个sheet就是数据,建议先打印sheet名和表头确认一下。

df = pd.read_excel('arwu_2022.xlsx', engine='openpyxl', sheet_name=0) print(df.head())

软科也有中文版和英文版表格,字段名不同,中文版是“学校名称”,英文版是“University”。为了统一脚本,我建议下载英文版,实在只能拿到中文版也没有关系,后面做字段映射时一并处理。

2.2 QS和THE:CSV顺手,但字段名各不相同

THE官网排名页面提供了CSV格式的数据,字段基本是Rank、Name、Country、Score之类,读起来比较直接。QS虽然有时需要从网页表格里复制,但官网排名页也会开放数据导出入口。重点提醒一句:QS页面里的Rank列经常出现=10这样的写法,THE在部分年份会把“501-600”这种区间直接塞进Rank列,这些都不能直接用int()转换。

df_the = pd.read_csv('the_2023.csv', encoding='utf-8-sig') df_qs = pd.read_csv('qs_2023.csv', encoding='utf-8-sig')

字段名在不同年份可能发生变化,不要写死。我每次拿到新数据都会先打印所有列名。

2.3 US News:必须抓网页时的正确姿势

US News的Best Global Universities ranking没有像软科那样直接的Excel下载,更多时候需要通过网页表格抓取。好在它的排名表是标准HTML表格,可以直接用pandas的read_html解析。页面如果做了分页,就循环拼接;如果页面比较慢,要设置headers和请求间隔。

tables = pd.read_html('usnews_2022.html') df = tables[0]

这里我的经验是先把网页保存成HTML文件再解析,而不是每次都实时请求。这样一方面减少被网站限制的风险,另一方面数据版本也更可控。抓取时设置一个常见的User-Agent,请求间隔控制在1秒以上,遇到验证码或访问异常就停下来,不要硬刚。

2.4 数据使用的合规底线

判断哪些能抓哪些不能抓,我的原则是:优先下载官方公开数据,其次用pd.read_html解析静态表格,绝不去绕过登录、验证码或付费墙。这些排名数据用于个人学习、课堂演示、博客分析没有问题,但如果要做商业发布或批量转售,必须对照各官网的使用条款。脚本里我也只在代码注释里写了数据来源,不附带任何抓取破解逻辑,省得给读者和自己惹麻烦。

3. 脚本设计的关键决策:字段映射、学校名统一和参数传递

3.1 字段到底怎么映射

四份数据的原始字段各不相同,合并前必须统一成一套标准字段。我常用的标准字段是:

rank_raw: 原始排名字符串,如 "=10"、"101-150" rank_int: 用于排序的整数排名 school: 学校标准名称 country: 国家/地区 score: 总分(如果有) source: 榜单来源:USNews / ARWU / QS / THE year: 版本年份

不同来源的原始字段要通过一个映射字典转换。

数据源原始排名字段学校名字段国家字段分数段
软科排名学校名称 / University国家/地区总分
QSRankUniversityLocationOverall
THERankNameCountryScore
US NewsRankUniversityLocationGlobal Score

这一步看起来简单,但我建议不要直接把原始列名改掉,而是保留一份原始数据,再加一列新字段。做数据清洗时最怕的是不可回溯。

3.2 学校名称统一是最大的体力活

四份数据合并时最耗时间的不是写代码,而是处理同一所学校的多种写法。同一所加州大学伯克利分校,在US News里写作University of California--Berkeley,软科英文版写作University of California, Berkeley,中文版直接写“加州大学伯克利分校”。如果你不做统一,合并后同一个学校会被拆成三条记录,分析结果完全不能用。

我的处理方案是三层兜底:先写一个normalize_name函数,把所有名称转成小写、去掉标点、压缩空格;再维护一个手动别名映射表,把确认过的别名指向标准名;最后跑完合并再抽检,看看哪些学校出现了多条记录。别指望正则一次性解决所有问题,教育机构名称的写法太散了,人工核对必不可少。

3.3 让主脚本通过命令行参数调度子脚本

涉及“python给另一个py脚本传递参数”的常见场景,就是我把项目拆成了多个脚本。主脚本负责调度,子脚本负责处理单个榜单,这样任何一个榜单更新了,我只重跑对应的子脚本就行。

python main.py --year 2022 --sources qs the arwu usnews --output merged_2022.csv python process_single_rank.py --source qs --year 2022 --output data/qs_2022_clean.csv

在Python里,子脚本内部用argparse解析参数,主脚本用subprocess.run把参数传过去。

import subprocess subprocess.run([ 'python', 'process_single_rank.py', '--source', 'arwu', '--year', '2022', '--output', 'data/arwu_clean.csv' ])

也完全可以不通过子进程,直接把子脚本写成函数,用函数参数传递sourceyear。我之所以用命令行拼接,是因为这样每个子脚本都能独立运行,调试单个数据源时不需要把整个流程跑一遍。

4. 可运行的完整示例:四榜合并的Python实现

下面是我这个项目里精简过的核心代码,可以直接跑通“读取四个数据源 -> 清洗标准化 -> 合并导出”的完整流程。你只需要把下载好的原始文件放进data目录,然后运行命令行参数。

import argparse import re import pandas as pd ALIASES = { 'university of california berkeley': 'university of california berkeley', 'university of california, berkeley': 'university of california berkeley', 'university of california--berkeley': 'university of california berkeley', 'university of cambridge': 'university of cambridge', 'university of oxford': 'university of oxford', } def parse_rank(raw): """ 将排名文本转成可排序的整数。 '=10' -> 10, '#5' -> 5, '101-150' -> 101 """ if pd.isna(raw): return None text = str(raw).strip().lstrip('=#') match = re.match(r'(\d+)', text) return int(match.group(1)) if match else None def normalize_name(name): if not isinstance(name, str): return '' text = name.lower() text = text.replace('--', ' ').replace(',', ' ') text = re.sub(r'[^a-z0-9\u4e00-\u9fff ]', '', text) text = re.sub(r'\s+', ' ', text).strip() return ALIASES.get(text, text) def safe_extract(df, mapping, columns): for target, src in mapping.items(): if src in df.columns: df[target] = df[src] else: df[target] = None return df[columns] def load_shanghai(path): df = pd.read_excel(path, engine='openpyxl') df = safe_extract(df, { 'rank_raw': '排名', 'school': '学校名称', 'country': '国家/地区', 'score': '总分', }, ['rank_raw', 'school', 'country', 'score']) df['source'] = 'ARWU' return df def load_qs(path): df = pd.read_html(path)[0] df = safe_extract(df, { 'rank_raw': 'Rank', 'school': 'University', 'country': 'Location', 'score': 'Overall', }, ['rank_raw', 'school', 'country', 'score']) df['source'] = 'QS' return df def load_the(path): df = pd.read_csv(path, encoding='utf-8-sig') df = safe_extract(df, { 'rank_raw': 'Rank', 'school': 'Name', 'country': 'Country', 'score': 'Score', }, ['rank_raw', 'school', 'country', 'score']) df['source'] = 'THE' return df def load_usnews(path): df = pd.read_html(path)[0] df = safe_extract(df, { 'rank_raw': 'Rank', 'school': 'University', 'country': 'Location', 'score': 'Global Score', }, ['rank_raw', 'school', 'country', 'score']) df['source'] = 'USNews' return df def merge_rankings(sources, year): loader_map = { 'arwu': load_shanghai, 'qs': load_qs, 'the': load_the, 'usnews': load_usnews, } frames = [] for source in sources: path = f'data/{source}_{year}.csv' if source == 'arwu': path = f'data/{source}_{year}.xlsx' print(f'loading {path}') frames.append(loader_map[source](path)) merged = pd.concat(frames, ignore_index=True) merged['rank_int'] = merged['rank_raw'].apply(parse_rank) merged['school_norm'] = merged['school'].apply(normalize_name) merged['year'] = year return merged def main(): parser = argparse.ArgumentParser(description='Merge university rankings') parser.add_argument('--year', default='2022') parser.add_argument('--sources', nargs='+', default=['arwu', 'qs', 'the', 'usnews']) parser.add_argument('--output', default='merged_rankings.csv') args = parser.parse_args() result = merge_rankings(args.sources, args.year) result.to_csv(args.output, index=False, encoding='utf-8-sig') print(f'saved: {args.output}, rows: {len(result)}') if __name__ == '__main__': main()

运行方式:

python merge_rankings.py --year 2022 --sources qs the arwu usnews --output all_2022.csv

这段代码里,safe_extract是我觉得比较重要的一个函数:遇到列名对不上的情况,它不会报错,而是填None。这是因为不同年份、不同版本的榜单,列名经常微调,留一个可见的缺失值比让整个脚本崩溃好很多。

5. 实测踩坑记录:区间排名、等号、编码和地区字段

5.1 “101-150”这类区间排名不能直接转整数

软科和THE都有区间排名,比如“101-150”“201-300”。如果你直接int('101-150'),Python会直接报错。我在parse_rank里用正则只取第一个数字,也就是把区间下限当作排序依据。这样处理之后,101-200排名的学校会排在101位,和真正第101名的学校并列,虽然不完美,但足够排序使用。

如果非要更精确一点,可以生成一个rank_display字段保留原始字符串,再生成rank_int用于排序。这样最终Excel里既能看到原始区间,也能参与排序和筛选。

data['rank_display'] = data['rank_raw'] data['rank_int'] = data['rank_raw'].apply(parse_rank)

5.2 QS的=号和THE的区间号

QS排名里=10表示并列第十,如果不处理,字符串转整数会失败。我在parse_rank中先lstrip('=#'),把等号和井号去掉。THE同样会有区间排名,甚至不同年份区间格式还不一样,有的写“501-600”,有的写“401–500”(注意中间是短横线不是标准连字符)。正则(\d+)能兼容这两种情况,因为它只要找到第一串数字就够了。

处理这些脏数据最关键的习惯是:永远先打印数据分布,看看rank_raw这个字段到底有多少种非数字写法。

5.3 CSV编码:肉眼看不见的BOM

从官网下载的CSV,很多是UTF-8带BOM的格式。如果用默认参数读,表头第一列会出现一个看不见的\ufeff字符,导致列名匹配不上。我在读CSV时固定用encoding='utf-8-sig',输出合并结果时也用utf-8-sig。这样文件在Excel里打开也不会乱码,中文学校名能正常显示。

5.4 地区字段的对照包袱

不同榜单对国家和地区的写法差异很大,US News和QS喜欢用“USA”“United States”,软科中文版直接写“美国”,THE有时写“United States of America”。香港和台湾的写法更是五花八门。我的做法是单独建一个地区映射字典,把常见写法统一成标准中文或标准英文,不要试图在清洗函数里写死所有分支。

5.5 学校名里的撇号与特殊符号

伦敦大学国王学院写作King's College London,正常化时会去掉撇号变成kings college london;但有些榜单写作Kings College London,有些写作King's College London,处理之后能对上。真正麻烦的是那些带特殊字符的欧洲学校,比如“Universität”和“Université”经过正则清洗后都会变成“universit”,正好落进同一批很难区分。遇到这种问题,我的建议是维护手动映射表,把这类学校在四个榜单的写法全部人工核对一遍。虽然累,但这是唯一可靠的办法。

6. 合并数据之后,我实际拿来做的三类分析

6.1 找偏科学校:谁在软科很强却不在QS榜单前列

合并表最有价值的应用之一,是快速识别“偏科”学校。比如加州大学旧金山分校是一所医学研究生院,在US News全球排名里能排在非常靠前的位置,但到了QS体系里因为主要依赖声誉和本科指标,排名一下子就跌到很难找到。用合并表做筛选,条件很简单:source == 'ARWU'rank_int < 50,同时source == 'QS'rank_int > 100。列出来之后,你对这些学校的定位会立刻清晰很多。这种“偏科学校”恰恰是很多人选校时最容易误解的地方。

6.2 中国高校在不同榜单中的排位差异

拿国内高校来说,清北复交、浙大、上交这些学校无论哪个榜单都稳居前列,但具体名次波动非常大。理工科强校在软科和US News里通常更靠前,因为这两个榜单更看重论文和引用;而QS因为雇主声誉权重高,综合性大学和商科强的学校表现会更亮眼。这些差异不是谁错了,而是指标选择的结果。把四列排名放到同一张表里看,比单独盯一个榜单容易理解得多。

我常用的一行代码是把每个学校在四个榜单的排名转成长表,再做一次透视:

pivot = merged.pivot_table( index='school_norm', columns='source', values='rank_int', aggfunc='min' )

这个透视结果就是一张校园对比表,下一步无论是排序还是画图都很方便。

6.3 稳定性分析:排名起伏很大的学校要留意

合并数据还能帮你判断一所学校的排名“性格”。有的学校四个榜单排名都稳定在前50,说明各方共识高;有的学校在QS排第30,在软科却排到第150,这种巨大落差本身就是信息。在学校名对齐之后,计算每个学校的排名极差或者标准差非常容易。

pivot['max_rank'] = pivot.max(axis=1) pivot['min_rank'] = pivot.min(axis=1) pivot['spread'] = pivot['max_rank'] - pivot['min_rank']

对于申请者来说,如果一所学校排名极差过大,你需要多确认一步:它是某一领域特别强导致的指标偏科,还是近年来实力波动比较大。纯粹追求排名稳定的读者,可以优先锁定那些四个榜单都排名靠前、极差又小的学校。

6.4 可视化扩展:散点图一眼看清格局

合并表同样适合可视化。用matplotlib或plotly画一张散点图,横轴是QS排名,纵轴是THE排名,每个点是一所学校,感兴趣的话还可以把点大小映射成软科排名。看到点的分布后,你会很直观地感受到不同榜单的差异程度。这个操作不需要写太多代码,但做出来的图很适合扔进PPT或者直接发到朋友圈。

7. 一点个人习惯

这套脚本后来我又改过好几版,合并逻辑一直没有大动。最后分享一个我踩坑之后养成的习惯:每次下载原始数据文件,我都会在文件名后加上榜单名和发布日期,例如qs_20230610.csvthe_20221012.csv。这样脚本处理出错时,我能根据日期快速回溯到对应版本,而不是对着一个叫final.csv的文件发呆。数据源随时可能改格式,脚本最怕的就是原始文件已经变了,而你还不知道。

本文还有配套的精品资源,点击获取

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

双种群遗传算法求解装配线平衡问题:从原理到代码实现

简介&#xff1a;双种群遗传算法解决装配线平衡问题的MATLAB实现&#xff0c;面向工业工程、运筹优化学习者与算法研究人员。该算法通过两个独立种群并行演化&#xff0c;增强全局搜索能力并抑制早熟收敛&#xff0c;适用于求解以最小化生产节拍为目标的工作站任务分配问题。压…

作者头像 李华
网站建设 2026/9/8 11:27:54

Fiddler抓包实战:从zip版安装到HTTPS解密与弱网模拟

简介&#xff1a;Fiddler 是广受开发者欢迎的网络调试工具&#xff0c;这份安装包面向需要抓包分析、接口调试与性能排查的 Web 前端、后端开发及测试人员&#xff0c;解决查看 HTTP/HTTPS 交互细节、定位接口异常、优化网页加载等常见问题。包内共 90 个文件&#xff0c;涵盖 …

作者头像 李华
网站建设 2026/9/8 11:26:39

AI Agent 如何决策:从 0.1% 胜率翻盘的搜索与评估逻辑

这是一场让我反复回看了很多遍的对局复盘。故障机器人在 A20 进阶难度下面对心魔&#xff0c;血量见底&#xff0c;牌组强度落后&#xff0c;胜率评估已经跌到 0.1% 以下。但 AI Agent 没有投降&#xff0c;它通过一轮又一轮的状态评估、目标拆解和行动搜索&#xff0c;在看似必…

作者头像 李华
网站建设 2026/9/8 11:26:00

散养牛羊智慧定位系统:GPS北斗+4G+电子围栏全解析

1. 方案整体设计与核心思路 1.1 为什么散养牛羊管控必须要上定位系统 先聊点实际的。养过散养牛羊的朋友都有体会&#xff0c;几十头牛撒到山坡上、林地里&#xff0c;一天要巡好几次。人力成本倒是其次&#xff0c;真正头疼的是丢牛找牛。我见过不少养殖户&#xff0c;一晚上…

作者头像 李华
网站建设 2026/9/8 11:25:41

AI对话SDK接入实战:从环境部署到机器人自然语言交互

机器人这个行业&#xff0c;过去大家拼的是运动控制、导航算法、机械臂精度&#xff0c;谁定位准、谁抓取稳&#xff0c;谁就有竞争力。但这几年风向变了&#xff1a;硬件差距在缩小&#xff0c;用户开始在意“对话体验”。同样是服务机器人&#xff0c;有的像对讲机&#xff0…

作者头像 李华
网站建设 2026/9/8 11:25:27

【单片机毕业设计】基于 STM32 的声光预警式人体健康运动监测装置设计 基于 STM32 的户外人员生理与运动定位监测系统设计(013307)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华