——写在前面——
软科排名是四大世界大学排行榜之一(其他三个是QS,TIMES,US News),相比于QS和TIMES,软科排名更侧重于评估学校的学术水平。因此,对于想出国读博/做博后/找学术职位的人来说,该排名可以作为选校时的一个重要参考指标。但软科排名有一个很大的缺点,就是如果某个学校不是世界前100,那么该学校的排名直接显示为范围(如101-150,201-300)。并且该范围内所有大学按照首字母的顺序排列,如Arizona State University已经连续几年排在101-150这个区间的第一名。软科自己的说法是某一范围内的这些大学水平都差不多(大概这么个意思)。不过话虽如此,软科给这些学校各个部分的打分却仍然有较明显的差别。根据软科自己的算法,我们完全可以对各部分的分数*权重后求和算出每个学校的总分,然后给他重新排个名。个人认为这样的排名才会更有意义一些。
——数据获取——
想要重新计算排名,首先就得获取数据,最简单的方法就是用python爬他们网站了。不过他们网站今年彻底重做了一次,导致我去年写的脚本已经完全失效了。因此只能重写代码。仔细看了看他们网站,发现两个问题:1. 每个页面只显示30所大学,且没有选项可以让整个页面显示所有大学;2. 翻页采用的是ajax(用的nuxt.js框架),也就是说翻页的时候网站url不会改变,那么也就无法通过暴力遍历所有page的url来爬取数据。我想了想,大致整理了两条思路,第一条是用selenium模拟人为操作获取数据,第二条就是看看有没有api可以用。先试了试第一条思路, 虽然能跑,但代码量稍微大了点。我就想:如果通过API的方式来提取数据,会不会更简洁高效一些?于是我百度了下相关的博文,试图找相关的API,结果看到这个博主分享了一个软科搞的中国大学排名的api(python爬虫小案例_中国大学排名(2021.04.11))。参考这个api,我就用chrome的开发者工具去世界大学排名页面的sources中的js文件中找所有的api字眼,结果真就找到了两个可用的api:
1. https://www.shanghairanking.com/api/pub/v1/arwu/rank?version=<year>
2. https://www.shanghairanking.com/api/pub/v1/inst/<univUp>
第一个api返回的数据含有你在排名页面看到的所有数据,每个大学对应的json数据结构如下:
{"ranking":"1","univNameEn":"Harvard University","univUp":"harvard-university","univLogo":"logo/032bd1b77.png","region":"United States","regionLogo":"us","regionRanking":"1","score":100,"indData":{"140":100,"141":100,"142":100,"143":100,"144":80.5,"145":100}}
其中,“ranking”是世界排名;“univNameEn”是学校名称;“univUp”可以理解为学校ID,结合第二个api使用可获取该学校的详细信息;“univLogo”是学校logo图片;“region”是地区;“regionLogo”是国旗;“regionRanking”是地区排名;“score”是总得分;“indData”是各项指标得分的集合,140是校友获奖得分(Alumini),141是教师获奖得分(Award),142是高被引科学家得分(HiCi),143是NS论文得分(N&S),144是师均表现(PCP),145是国际论文得分(PUB)。知晓了以上信息后,我们就把问题转变为了从json文件中提取数据。那就简单了:
import requests
import json
url='https://www.shanghairanking.com/api/pub/v1/arwu/rank?version=2021'
def get_url(url):
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
'Referer':'https://shanghairanking.com/',
'DNT':'1',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Connection':'keep-alive',
'Accept-Language':'zh-CN,zh;q=0.9,en-CN;q=0.8,en;q=0.7',
'Accept-Encoding':'gzip, deflate',
}
req = requests.get(url, headers=headers)
req.encoding='utf-8'
html = req.json()
return html
dataset = get_url(url)['data']['rankings']
idx = len(dataset)
f = open('arwu_2021.txt', 'w', encoding='utf-8')
f.write('World-Rank' + '\t' + 'Name' + '\t' + 'Region' + '\t' + 'Alumini' + '\t' + 'Award' + '\t' + 'HiCi' + '\t' + 'N&S' + '\t' + 'PCP' + '\t' + 'PUB' + '\t' + 'Total' + '\n')
for i in range(idx):
WorldRank = dataset[i]['ranking']
UniName = dataset[i]['univNameEn']
Region = dataset[i]['region']
Alumini = dataset[i]['indData']['140']
Award = dataset[i]['indData']['141']
HiCi = dataset[i]['indData']['142']
NS = dataset[i]['indData']['143']
PCP = dataset[i]['indData']['144']
PUB = dataset[i]['indData']['145']
Total = dataset[i]['score']
summary = str(WorldRank) + '\t' + str(UniName) + '\t' + str(Region) + '\t' + str(Alumini) + '\t' + str(Award) + '\t' + str(HiCi) + '\t' + str(NS) + '\t' + str(PCP) + '\t' + str(PUB) + '\t' + str(Total) + '\n'
f.write(summary)
f.close()
数据爬下来后,可以直接弄到excel里面算总分,至于权重比例可参考官网的排名方法。当然,如果你懒得爬我这边也整理好了一份,可自行下载。