爬蟲對象選擇
打開軟科中國大學排名,觀察這個頁面結構復雜且一頁只顯示了 30 所大學。
而且點擊了翻頁,發現 url 沒有發生變化,說明該頁面有可能是已經獲取了所有數據,然后使用 JavaScript 動態生成。此時可能就很麻煩了,因為沒有辦法通過 get 傳參的方式來切換網頁進行爬取。
從開發者工具中的“network”可以看到,大量的 JavaScript 腳本被執行。
查看“fetch/XHR”,fetch 和 XHR 都是是獲取遠端數據的方式,fetch 是原生 js 方法,XMLHttpRequest(XHR)是一個構造函數,通過 XMLHttpRequest 可以在不刷新頁面的情況下請求特定 URL。但是無論如何這 2 種方式都是向后端獲取數據的,但是依次查看這些信息也沒有找到所有大學的數據。
因此接下來就需要去找到底是哪個接口能找到這些數據,在開發者工具中搜索隨便一所大學,例如清華大學。可以看到在 2 個資源中出現過這個字符串,第一個是“軟科中國大學排名”的頁面,也就是我們進入網頁后能看得到的。
第二個出現在“payload.js”文件中,可以看到在 univData 中出現了清華大學和其他大學的參數,說明這個 JavaScript 文件保存了所有大學的信息。(使用谷歌瀏覽器和火狐瀏覽器,在開發者工具中可以自動格式化解析這些網絡資源,看着比較友好,用其他的瀏覽器也可以,不過看到的可能是沒有編碼過的,但是不影響分析)。
訪問“payload.js”文件資源,可以看到這些數據確實存在此處,所以這就是我們要爬蟲的對象。
https://www.shanghairanking.cn/_nuxt/static/1635996352/rankings/bcur/2021/payload.js
使用 requests 庫進行爬取,編寫代碼如下:
url = 'https://www.shanghairanking.cn/_nuxt/static/1635996352/rankings/bcur/2021/payload.js'
r = requests.get(url, timeout=20)
if r.status_code == 200:
r.encoding = 'utf-8'
content = r.text
數據處理
我們把“payload.js”文件爬下來以后,是不能直接用 JSON 進行解析的,因為這個文件中摻雜了 JavaScript 的代碼。
同時這些鍵值對中,鍵的部分沒有用括號進行包括,此時對於 Python 而言會認為這是個變量。無論是用 json 庫還是 eval 都不能直接解析,都會報錯。
所以比較好的方式就是直接處理字符串,首先這些數據的順序就是排名的順序,因此按順序提取大學的信息可以得到排名。例如此處我還想獲取這些大學的省份信息,除了要切分出“univNameCn”字段信息,還要切分出“province”字段。(軟科排名網站使用一個字母代表一個省份或者市,例如“v”代表北京市,“N”代表福建省)
根據實際需求,使用字符串切分的方式提取出我們想要的信息,例如:
colleges = []
while content.find('univNameCn:"') != -1:
acollege = []
#切分出大學名稱
content = content[content.find('univNameCn:"') + 12:]
collegeName = content[:content.find('"')]
acollege.append(collegeName)
#切分出省份
content = content[content.find('province:') + 9:]
province = content[:content.find(',')]
acollege.append(province)
colleges.append(acollege)
完整代碼
例如我要輸出軟科排名前 10 的福建省大學是哪些,一個簡單的爬蟲 demo 如下:
import requests
url = 'https://www.shanghairanking.cn/_nuxt/static/1635996352/rankings/bcur/2021/payload.js'
r = requests.get(url, timeout=20)
if r.status_code == 200:
r.encoding = 'utf-8'
content = r.text
colleges = []
while content.find('univNameCn:"') != -1:
acollege = []
#切分出大學名稱
content = content[content.find('univNameCn:"') + 12:]
collegeName = content[:content.find('"')]
acollege.append(collegeName)
#切分出省份
content = content[content.find('province:') + 9:]
province = content[:content.find(',')]
acollege.append(province)
colleges.append(acollege)
print("{:^10}\t{:^6}".format("排名","學校名稱"))
num = 10
for i in range(len(colleges)):
if colleges[i][1] == 'N':
print("{:^10}\t{:^6}".format(str(i + 1), colleges[i][0]))
num -= 1
if num == 0:
break
輸出的結果如下所示:
從 API 獲取數據
例如在 2021 年 4 月份,有其他的博主通過 network 找到了軟科排名網站返回數據的 API,例如可以參考《python爬蟲小案例_中國大學排名(2021.04.11)》這篇博客。
https://www.shanghairanking.cn/api/pub/v1/bcur?bcur_type=11&year=2021
從這個 API 中可以獲取某一年份的大學排名信息,且不存在編碼問題,鍵值對也是字符串可以直接被 json 解析。但是現在我直接用開發者工具找是找不到了,不過這個 API 還能用。當網頁結構發生改變之時,如果以前的方法不能用了就得另找其他途徑了。