分類
發燒車訊

python動態柱狀圖圖表可視化:歷年軟科中國大學排行

本來想參照:https://mp.weixin.qq.com/s/e7Wd7aEatcLFGgJUDkg-EQ搞一個往年編程語言動態圖的,奈何找不到數據,有數據來源的歡迎在評論區留言。

這裏找到了一個,是2020年6月的編程語言排行,供大家看一下:https://www.tiobe.com/tiobe-index/

 

我們要實現的效果是:

大學排名來源:http://www.zuihaodaxue.com/ARWU2003.html

部分截圖:

在http://www.zuihaodaxue.com/ARWU2003.html中的年份可以選擇,我們解析的頁面就有了:

"http://www.zuihaodaxue.com/ARWU%s.html" % str(year)

初步獲取頁面的html信息的代碼:

def get_one_page(year):
    try:
        headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
            }
        url = "http://www.zuihaodaxue.com/ARWU%s.html" % str(year)
        response=requests.get(url,headers=headers)
        if response.status_code == 200:
            return response.content
    except RequestException:
        print('爬取失敗')

我們在頁面上進行檢查:

數據是存儲在表格中的,這樣我們就可以利用pandas獲取html中的數據,基本語法:

tb = pd.read_html(url)[num]

其中的num是標識網頁中的第幾個表格,這裏只有一個表格,所以標識為0。初步的解析代碼就有了:

def parse_on_page(html,i):
    tb=pd.read_html(html)[0]
    return tb

我們還要將爬取下來的數據存儲到csv文件中,基本代碼如下:

def save_csv(tb):
    start_time=time.time()
    tb.to_csv(r'university.csv', mode='a', encoding='utf_8_sig', header=True, index=0)
    endtime = time.time()-start_time
    print('程序運行了%.2f秒' %endtime)

最後是一個主函數,別忘了還有需要導入的包:

import requests
from requests.exceptions import RequestException
import pandas as pd
import time
def main(year):
    for i in range(2003,year):
        html=get_one_page(i)
        tb=parse_on_page(html,i)
        #print(tb)
        save_csv(tb)
if __name__ == "__main__":
    main(2004)

運行之後,我們在同級目錄下就可以看到university.csv,部分內容如下:

存在幾個問題:

(1)缺少年份

(2)最後一列沒有用

(3)國家由於是圖片表示,沒有爬取下來

(4)排名100以後的是一個區間

我們接下來一一解決:

(1)刪掉沒用的列

def parse_on_page(html,i):
    tb=pd.read_html(html)[0]
    # 重命名表格列,不需要的列用數字錶示
    tb.columns = ['world rank','university', 2, 'score',4]
    tb.drop([2,4],axis=1,inplace=True)
    return tb

新的結果:

(2) 對100以後的進行唯一化,增加一列index作為排名標識

tb['index_rank'] = tb.index
tb['index_rank'] = tb['index_rank'].astype(int) + 1

(3)新增加年份

tb['year'] = i

(4)新增加國家

首先我們進行檢查:

發現國家在td->a>img下的圖像路徑中有名字:UnitedStates。 我們可以取出src屬性,並用正則匹配名字即可。

def get_country(html):
    soup = BeautifulSoup(html,'lxml')
    countries = soup.select('td > a > img')
    lst = []
    for i in countries:
        src = i['src']
        pattern = re.compile('flag.*\/(.*?).png')
        country = re.findall(pattern,src)[0]
        lst.append(country)
    return lst

然後這麼使用:

# read_html沒有爬取country,需定義函數單獨爬取
tb['country'] = get_country(html)

最終解析的整體函數如下:

def parse_on_page(html,i):
    tb=pd.read_html(html)[0]
    # 重命名表格列,不需要的列用數字錶示
    tb.columns = ['world rank','university', 2, 'score',4]
    tb.drop([2,4],axis=1,inplace=True)
    tb['index_rank'] = tb.index
    tb['index_rank'] = tb['index_rank'].astype(int) + 1
    tb['year'] = i
    # read_html沒有爬取country,需定義函數單獨爬取
    tb['country'] = get_country(html)
    return tb

運行之後:

最後我們要提取屬於中國部分的相關信息:

首先將年份改一下,獲取到2019年為止的信息:

if __name__ == "__main__":
    main(2019)

然後我們提取到中國高校的信息,直接看代碼理解:

def analysis():
    df = pd.read_csv('university.csv')
    # 包含港澳台
    # df = df.query("(country == 'China')|(country == 'China-hk')|(country == 'China-tw')|(country == 'China-HongKong')|(country == 'China-Taiwan')|(country == 'Taiwan,China')|(country == 'HongKong,China')")[['university','year','index_rank']]

    # 只包括內地
    df = df.query("(country == 'China')")
    df['index_rank_score'] = df['index_rank']
    # 將index_rank列轉為整形
    df['index_rank'] = df['index_rank'].astype(int)

    # 美國
    # df = df.query("(country == 'UnitedStates')|(country == 'USA')")

    #求topn名
    def topn(df):
        top = df.sort_values(['year','index_rank'],ascending = True)
        return top[:20].reset_index()
    df = df.groupby(by =['year']).apply(topn)

    # 更改列順序
    df = df[['university','index_rank_score','index_rank','year']]
    # 重命名列
    df.rename (columns = {'university':'name','index_rank_score':'type','index_rank':'value','year':'date'},inplace = True)

    # 輸出結果
    df.to_csv('university_ranking.csv',mode ='w',encoding='utf_8_sig', header=True, index=False)
    # index可以設置

本來是想爬取從2003年到2019年的,運行時發現從2005年開始,頁面不一樣了,多了一列:

方便起見,我們就只從2005年開始了,還需要修改一下代碼:

    # 重命名表格列,不需要的列用數字錶示
    tb.columns = ['world rank','university', 2,3, 'score',5]
    tb.drop([2,3,5],axis=1,inplace=True)

最後是整體代碼:

import requests
from requests.exceptions import RequestException
import pandas as pd
import time
from bs4 import BeautifulSoup
import re
def get_one_page(year):
    try:
        headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
            }
        url = "http://www.zuihaodaxue.com/ARWU%s.html" % str(year)
        response=requests.get(url,headers=headers)
        if response.status_code == 200:
            return response.content
    except RequestException:
        print('爬取失敗')
def parse_on_page(html,i):
    tb=pd.read_html(html)[0]
    # 重命名表格列,不需要的列用數字錶示
    tb.columns = ['world rank','university', 2,3, 'score',5]
    tb.drop([2,3,5],axis=1,inplace=True)
    tb['index_rank'] = tb.index
    tb['index_rank'] = tb['index_rank'].astype(int) + 1
    tb['year'] = i
    # read_html沒有爬取country,需定義函數單獨爬取
    tb['country'] = get_country(html)
    return tb
def save_csv(tb):
    start_time=time.time()
    tb.to_csv(r'university.csv', mode='a', encoding='utf_8_sig', header=True, index=0)
    endtime = time.time()-start_time
    print('程序運行了%.2f秒' %endtime)
# 提取國家名稱
def get_country(html):
    soup = BeautifulSoup(html,'lxml')
    countries = soup.select('td > a > img')
    lst = []
    for i in countries:
        src = i['src']
        pattern = re.compile('flag.*\/(.*?).png')
        country = re.findall(pattern,src)[0]
        lst.append(country)
    return lst
def analysis():
    df = pd.read_csv('university.csv')
    # 包含港澳台
    # df = df.query("(country == 'China')|(country == 'China-hk')|(country == 'China-tw')|(country == 'China-HongKong')|(country == 'China-Taiwan')|(country == 'Taiwan,China')|(country == 'HongKong,China')")[['university','year','index_rank']]

    # 只包括內地
    df = df.query("(country == 'China')")
    df['index_rank_score'] = df['index_rank']
    # 將index_rank列轉為整形
    df['index_rank'] = df['index_rank'].astype(int)

    # 美國
    # df = df.query("(country == 'UnitedStates')|(country == 'USA')")

    #求topn名
    def topn(df):
        top = df.sort_values(['year','index_rank'],ascending = True)
        return top[:20].reset_index()
    df = df.groupby(by =['year']).apply(topn)

    # 更改列順序
    df = df[['university','index_rank_score','index_rank','year']]
    # 重命名列
    df.rename (columns = {'university':'name','index_rank_score':'type','index_rank':'value','year':'date'},inplace = True)

    # 輸出結果
    df.to_csv('university_ranking.csv',mode ='w',encoding='utf_8_sig', header=True, index=False)
    # index可以設置
def main(year):
    for i in range(2005,year):
        html=get_one_page(i)
        tb=parse_on_page(html,i)
        save_csv(tb)
        print(i,'年排名提取完成完成')
        analysis()
if __name__ == "__main__":
    main(2019)

運行之後會有一個university_ranking.csv,部分內容如下:

接下來就是可視化過程了。

1、 首先,到作者的github主頁:  
https://github.com/Jannchie/Historical-ranking-data-visualization-based-on-d3.js

2、克隆倉庫文件,使用git

# 克隆項目倉庫
git clone https://github.com/Jannchie/Historical-ranking-data-visualization-based-on-d3.js
# 切換到項目根目錄
cd Historical-ranking-data-visualization-based-on-d3.js
# 安裝依賴
npm install

這裏如果git clone超時可參考:

https://www.cnblogs.com/xiximayou/p/12305209.html

需要注意的是,這裏的npm是我之前裝node.js裝了的,沒有的自己需要裝一下。

在執行npm install時會報錯:

先執行:

npm init

之後一直回車即可:

再執行npm install

任意瀏覽器打開bargraph.html網頁,點擊選擇文件,然後選擇前面輸出的university_ranking.csv文件,看下效果:

只能製作動圖上傳了。

可以看到,有了大致的可視化效果,但還存在很多瑕疵,比如:表順序顛倒了、字體不合適、配色太花哨等。可不可以修改呢?

當然是可以的,只需要分別修改文件夾中這幾個文件的參數就可以了:

  • config.js 全局設置各項功能的開關,比如配色、字體、文字名稱、反轉圖表等等功能;

  • color.css 修改柱形圖的配色;

  • stylesheet.css 具體修改配色、字體、文字名稱等的css樣式;

  • visual.js 更進一步的修改,比如圖表的透明度等。

知道在哪裡修改了以後,那麼,如何修改呢?很簡單,只需要簡單的幾步就可以實現:

  • 打開網頁,右鍵-檢查,箭頭指向想要修改的元素,然後在右側的css樣式表裡,雙擊各項參數修改參數,修改完元素就會發生變化,可以不斷微調,直至滿意為止。

    

  • 把參數複製到四個文件中對應的文件里並保存。

  • Git Bash運行npm run build,之後刷新網頁就可以看到優化后的效果。(我發現這一步其實不需要,而且會報錯,我直接修改config.js之後運行也成功了)

這裏我主要修改的是config.js的以下項:

  // 倒序,使得最短的條位於最上方 
  reverse: true,
  // 附加信息內容。
  // left label
  itemLabel: "本年度第一大學",
  // right label
  typeLabel: "世界排名",
  //為了避免名稱重疊
  item_x: 500,
  // 時間標籤坐標。建議x:1000 y:-50開始嘗試,默認位置為x:null,y:null
  dateLabel_x: 1000,
  dateLabel_y: -50,

最終效果:

至此,就全部完成了。

看起來簡單,還是得要自己動手才行。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準