Python爬取OPGG里英雄聯盟位置排名數據,及其可視化


一、選題背景

近年來電子競技在當今社會越來越來受歡迎,同時電子競技也成了亞運會項目之一。英雄聯盟便是奧運會上的項目類型之一,我國便曾在亞運會的英雄聯盟項目上拿下冠軍。所以我便選擇了,英雄聯盟這個項目來作為我的設計目標。

二、主題式網絡爬蟲設計方案

1.主題式網絡爬蟲名稱

OPGG里英雄聯盟位置排名數據,及其可視化

2.主題式網絡爬蟲爬取的內容與數據特征分析

 爬取opgg中上單及射手的排名,名字,勝率,出場率,並對其進行分析。

 數據來源:" http://www.op.gg/champion/statistics"

 

3.主題式網絡爬蟲設計方案概述

 (1)實現思路

     先對目標頁面進行分析,利用urllib.爬蟲庫和BeautifulSoup庫進行爬取解析,后分別用BeautifulSoup和正則表達式,分別查找所需要的數據。然后再保存為.csv文件,最后進行可視化分析。

 (2)技術難點

  request庫出現問題,被迫學習使用urllib.request庫,在編寫re庫的正則表達式中,發現自己熟練程度低,出錯較多,在數據可視化上也出現了忘記代碼的問題。

三、主題頁面的結構特征分析

1.主題頁面的結構與特征分析

首先得了解到本次爬取的網頁為” http://www.op.gg/champion/statistics

首先是本機的usr-agent查詢,做准備

 

 

由網站界面可以看出,右側有英雄的詳細信息,以Garen為例,勝率為53.84%,選取率為16.99%,常用位置為上單。

2.Htmls 頁面解析

現對網頁源代碼進行分析

代碼中共有5個tbody標簽(tbody標簽開頭結尾均有”tbody”,故共有10個”tbody”),對字段內容分析,分別為上單、打野、中單、ADC、輔助信息。

再對tbody標簽進行查找

 

 

 

 由此代碼可看出,英雄名、勝率及選取率都在td標簽中,而每一個英雄信息在一個tr標簽中,td父標簽為tr標簽,tr父標簽為tbody標簽

 

 

3.節點(標簽)查找方法與遍歷方法

計划將Beautifulsoup查找,re庫.正則表達式搭配查找

四、網絡爬蟲程序設計

1.數據爬取與采集 

 1 def askurl(urlbase):  2     import.urllib.request 引用urllib.request庫  3     #模擬瀏覽器頭部信息,向網站發送信息
 4     header={  5         "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62"
 6  }  7     #模擬用戶代理
 8     request = urllib.request.Request(urlbase,headers=header)  9     #異常超時處理
10     try: 11         # html=""
12         response = urllib.request.urlopen(request,timeout=5) 13         html = response.read().decode("utf-8") 14         # print(html)
15     except Exception as a: 16         print(a) 17     return html

2.數據解析與整理

(1)采用BeautifulSoup解析提取數據

 1 import pandas as pd  #導入pandas庫
 2 import bs4  # 導入bs4庫
 3 from bs4 import BeautifulSoup  # 導入BeautifulSoup庫
 4 url = "http://www.op.gg/champion/statistics"
 5 # 獲得html文檔信息
 6 html = askurl(url)  7 #解析數據
 8 soup = BeautifulSoup(html,"html.parser")  9 top = [] 10 name = []       #建立空列表用於儲存數據
11 winRate = [] 12 pickRate = [] 13 # 遍歷上單tbody標簽的兒子標簽
14 for tr in soup.find(name = "tbody",attrs = "tabItem champion-trend-tier-TOP").children: 15     # 判斷tr是否為標簽類型,去除空行
16     if isinstance(tr,bs4.element.Tag): 17         # 查找tr標簽下的td標簽
18         tds = tr('td') 19         #排名
20  top.append(tds[0].string) 21         # 英雄名
22         name.append(tds[3].find(attrs = "champion-index-table__name").string) 23         # 勝率 %百分號對后續有影響,去除
24         winRate.append(tds[4].string.replace('%','')) 25         # 選取率
26         pickRate.append(tds[5].string.replace('%','')) 27 
28 #將准確獲得的數據保存到列表中
29 df1 = pd.DataFrame(data=[top, name, winRate, pickRate], index=['排名', '英雄名', '英雄勝率', '英雄出場率']) 30 # 對文本進行,行換列,列換行
31 df2 = pd.DataFrame(df1.values.T, columns=df1.index) 32 # 保存數據到xlsx文件中
33 df2.to_excel('上單top.xlsx')

 

(2)利用正則提取法提取數據

 1 from bs4 import BeautifulSoup  2 import re  3 import pandas as pd  4 ADtop = []  5 ADname = []  # 設立空表格
 6 ADwinrate = []  7 ADpickrate = []  8 url = "http://www.op.gg/champion/statistics"
 9 # 獲得html文檔信息
10 html = askurl(url) 11 # 解析數據
12 soup = BeautifulSoup(html, "html.parser") 13 # 取得射手標簽內容
14 ADCdata = soup.find_all("tbody", class_="tabItem champion-trend-tier-ADC") 15 # 將BeautifulSoup類型轉換為字符串類型,以便使用re庫下的正則搜索
16 strADCdata = str(ADCdata) 17 # 通過網頁源碼得知,需要收集的排名,英雄名信息為23條
18 for i in range(0, 23): 19     # 排名 設定約束
20     findTop = re.compile(r'<td class="champion-index-table__cell champion-index-table__cell--rank">(\d*?)</td>') 21     # 運用約束查找
22  ADtop.append(re.findall(findTop, strADCdata)[i]) 23     # 英雄名字 設定約束
24     findName = re.compile(r'<div class="champion-index-table__name">(.*?)</div>') 25     # 運用約束查找
26  ADname.append(re.findall(findName, strADCdata)[i]) 27 # 運用正則表達式查找時發現勝率與選取率除了標簽,內容完全相同,導致在上面的循環中會出現交叉獲得的問題出現。
28 for i in range(0, 46, 2): 29     # 英雄勝率 設定約束 獲得偶數的勝率
30     findWin = re.compile( 31         r'<td class="champion-index-table__cell champion-index-table__cell--value">(\d{0,3}\.\d{2})%</td>') 32  ADwinrate.append(re.findall(findWin, strADCdata)[i]) 33     # 英雄出場率 設定約束 獲得奇數的出場率
34     findappear = re.compile( 35         r'<td class="champion-index-table__cell champion-index-table__cell--value">(\d{0,3}\.\d{2})%</td') 36     ADpickrate.append(re.findall(findappear, strADCdata)[i + 1]) 37 
38 # 將數據保存到列表中
39 df3 = pd.DataFrame(data=[ADtop, ADname, ADwinrate, ADpickrate], index=['排名', '英雄名', '英雄勝率', '英雄出場率']) 40 # 對文本進行,行換列,列換行
41 df4 = pd.DataFrame(df3.values.T, columns=df3.index) 42 # 保存數據到xlsx文件中
43 df4.to_excel('射手top.xlsx')

 

 

3.對數據進行清洗和處理

 

 TOPrank = pd.DataFrame(pd.read_excel('上單top.xlsx'))
    print(TOPrank.head())

    # #無無效列

    # 檢查是否有重復值
    print(TOPrank.duplicated())

    # 檢查是否有空值
    print(TOPrank['排名'].isnull().value_counts())

    # 異常值處理
    print(TOPrank.describe())
    # 發現“排名”字段的最大值為56而平均值為28,假設異常值為56
    # print(top.replace([56, top['排名'].mean()]))

 

  

 

 

 

 

 

 

 

4.數據分析與可視化

1:運用pyecharts制作表格視圖

 1 import pandas as pd  2 from pyecharts.components import Table  3 from pyecharts.options import ComponentTitleOpts  4 
 5 # 分別導入上單數據
 6 df_top = pd.read_excel('上單top.xlsx')  7 TOPTop = df_top['排名'].values.tolist()  8 TOPName = df_top['英雄名'].values.tolist()  9 TOPWin = df_top['英雄勝率'].values.tolist() 10 TOPpick = df_top['英雄出場率'].values.tolist() 11 
12 #繪制上單表格視圖
13 table2 = Table() 14 headers2 = ["排名", "英雄名", "英雄勝率", "英雄出場率"] 15 rows2 = [ 16 
17 ] 18 for i in range(0, 56): 19     rows2.append(df_top.iloc[i].values.tolist()[1:]) #轉換插入格式
20 table2.add(headers2, rows2) #插入數據
21 table2.set_global_opts( 22     title_opts=ComponentTitleOpts(title="上單-強度排行", subtitle="實時更新") #設置標題與副標題
23 ) 24 table2.render("上單pyecharts表格.html") 

 

 

 2:運用pyecharts制作柱狀圖

 1 import pandas as pd  2 from pyecharts.charts import Bar  3 from pyecharts.globals import ThemeType  4 from pyecharts.options import global_options as opts  5 
 6 # 分別導入上單數據
 7 df_top = pd.read_excel('上單top.xlsx')  8 TOPTop = df_top['排名'].values.tolist()  9 TOPName = df_top['英雄名'].values.tolist() 10 TOPWin = df_top['英雄勝率'].values.tolist() 11 TOPpick = df_top['英雄出場率'].values.tolist() 12 
13 # 繪制上單柱狀圖
14 c = ( 15     Bar({"theme": ThemeType.MACARONS}) 16         .add_xaxis(TOPName)  # 設置x軸
17         .add_yaxis("英雄勝率", TOPWin)  # 添加柱狀體
18         .add_yaxis("英雄出場率", TOPpick) 19  .set_global_opts( 20         title_opts={"text": "上單強度-示意圖", "subtext": "綜合勝率與出場率"},  # 設置標題與副標題
21         datazoom_opts=opts.DataZoomOpts(),  # 分段
22         xaxis_opts=opts.AxisOpts(name_rotate=60, name="英雄名", axislabel_opts={"rotate": 35})  # 字體傾斜角度
23 
24  ) 25         .render("上單強度柱狀圖.html") 26 )

 

 

 

3:運用pyecharts制作折線圖

 1 import pandas as pd  2 import pyecharts.options as opts  3 from pyecharts.charts import Line  4 # 分別導入上單數據
 5 df_top = pd.read_excel('上單top.xlsx')  6 TOPTop = df_top['排名'].values.tolist()  7 TOPName = df_top['英雄名'].values.tolist()  8 TOPWin = df_top['英雄勝率'].values.tolist()  9 TOPpick = df_top['英雄出場率'].values.tolist() 10 ( 11  Line() 12  .set_global_opts( 13         tooltip_opts=opts.TooltipOpts(is_show=False), 14         xaxis_opts=opts.AxisOpts(type_="category"), 15         yaxis_opts=opts.AxisOpts( 16             type_="value", 17             axistick_opts=opts.AxisTickOpts(is_show=True), 18             splitline_opts=opts.SplitLineOpts(is_show=True), 19  ), 20  ) 21     .add_xaxis(xaxis_data=TOPName) 22  .add_yaxis( 23         series_name="", 24         y_axis=TOPWin, 25         symbol="emptyCircle", 26         is_symbol_show=True, 27         label_opts=opts.LabelOpts(is_show=False), 28  ) 29  .set_global_opts( 30         title_opts={"text": "上單英雄-對應勝率曲線", "subtext": "強度曲線"} , 31         datazoom_opts=opts.DataZoomOpts(),#分段
32                          xaxis_opts=opts.AxisOpts(name_rotate=60, name="英雄名", axislabel_opts={"rotate": 35})#字體傾斜角度
33  ) 34     .render("上單勝率折線圖.html") 35 )

 

 

 

5.完整代碼

 1 # -*- coding = utf-8 -*-
 2 # @Time : 2021/12/24 14:49
 3 # @Author : 真建彬
 4 # @Student Number: 2003010221
 5 # @File : demo2.py
 6 
 7 def askurl(urlbase):  8     import urllib.request  9     #模擬瀏覽器頭部信息,向網站發送信息
 10     header={  11         "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62"
 12  }  13     #模擬用戶代理
 14     request = urllib.request.Request(urlbase,headers=header)  15     #異常超時處理
 16     try:  17         # html=""
 18         response = urllib.request.urlopen(request,timeout=5)  19         html = response.read().decode("utf-8")  20         # print(html)
 21     except Exception as a:  22         print(a)  23     return html  24 
 25 def main():  26     from pyecharts.charts import Bar  27     from pyecharts.globals import ThemeType  28     import re  29     import pandas as pd  30     import bs4  # 導入bs4庫
 31     from bs4 import BeautifulSoup  # 導入BeautifulSoup庫
 32     import pyecharts.options as opts  33     from pyecharts.charts import Line  34     from pyecharts.components import Table  35     from pyecharts.options import ComponentTitleOpts  36     url = "http://www.op.gg/champion/statistics"
 37     # 獲得html文檔信息
 38     html = askurl(url)  39     #解析數據
 40     soup = BeautifulSoup(html,"html.parser")  41     top = []  42     name = []        #建立空列表用於儲存數據
 43     winRate = []  44     pickRate = []  45     # 遍歷上單tbody標簽的兒子標簽
 46     for tr in soup.find(name = "tbody",attrs = "tabItem champion-trend-tier-TOP").children:  47         # 判斷tr是否為標簽類型,去除空行
 48         if isinstance(tr,bs4.element.Tag):  49             # 查找tr標簽下的td標簽
 50             tds = tr('td')  51             #排名
 52  top.append(tds[0].string)  53             # 英雄名
 54             name.append(tds[3].find(attrs = "champion-index-table__name").string)  55             # 勝率 %百分號對后續有影響,去除
 56             winRate.append(tds[4].string.replace('%',''))  57             # 選取率
 58             pickRate.append(tds[5].string.replace('%',''))  59 
 60     #將准確獲得的數據保存到列表中
 61     df1 = pd.DataFrame(data=[top, name, winRate, pickRate], index=['排名', '英雄名', '英雄勝率', '英雄出場率'])  62     # 對文本進行,行換列,列換行
 63     df2 = pd.DataFrame(df1.values.T, columns=df1.index)  64     # 保存數據到xlsx文件中
 65     df2.to_excel('上單top.xlsx')  66     ##讀取文件
 67     TOPrank = pd.DataFrame(pd.read_excel('上單top.xlsx'))  68     print(TOPrank.head())  69 
 70     # #無無效列
 71 
 72     # 檢查是否有重復值
 73     print(TOPrank.duplicated())  74 
 75     # 檢查是否有空值
 76     print(TOPrank['排名'].isnull().value_counts())  77 
 78     # 異常值處理
 79     print(TOPrank.describe())  80     # 發現“排名”字段的最大值為56而平均值為28,假設異常值為56
 81     # print(top.replace([56, top['排名'].mean()]))
 82 
 83 
 84 
 85     ADtop=[]  86     ADname=[]               #設立空表格
 87     ADwinrate=[]  88     ADpickrate=[]  89     #取得射手標簽內容
 90     ADCdata = soup.find_all("tbody", class_="tabItem champion-trend-tier-ADC")  91     #將BeautifulSoup類型轉換為字符串類型,以便使用re庫下的正則搜索
 92     strADCdata = str(ADCdata)  93     #通過網頁源碼得知,需要收集的排名,英雄名信息為23條
 94     for i in range(0,23):  95         #排名 設定約束
 96         findTop = re.compile(r'<td class="champion-index-table__cell champion-index-table__cell--rank">(\d*?)</td>')  97         #運用約束查找
 98  ADtop.append(re.findall(findTop,strADCdata)[i])  99         #英雄名字 設定約束
100         findName=re.compile(r'<div class="champion-index-table__name">(.*?)</div>') 101         #運用約束查找
102  ADname.append(re.findall(findName,strADCdata)[i]) 103     #運用正則表達式查找時發現勝率與選取率除了標簽,內容完全相同,導致在上面的循環中會出現交叉獲得的問題出現。
104     for i in  range(0,46,2): 105         #英雄勝率 設定約束 獲得偶數的勝率
106         findWin=re.compile(r'<td class="champion-index-table__cell champion-index-table__cell--value">(\d{0,3}\.\d{2})%</td>') 107  ADwinrate.append(re.findall(findWin,strADCdata)[i]) 108         #英雄出場率 設定約束 獲得奇數的出場率
109         findappear=re.compile(r'<td class="champion-index-table__cell champion-index-table__cell--value">(\d{0,3}\.\d{2})%</td') 110         ADpickrate.append(re.findall(findappear,strADCdata)[i+1]) 111 
112     # 將數據保存到列表中
113     df3 =pd.DataFrame(data=[ADtop, ADname, ADwinrate, ADpickrate], index=['排名','英雄名','英雄勝率','英雄出場率']) 114     # 對文本進行,行換列,列換行
115     df4 = pd.DataFrame(df3.values.T, columns=df3.index) 116     # 保存數據到xlsx文件中
117     df4.to_excel('射手top.xlsx') 118 
119     ADCrank = pd.DataFrame(pd.read_excel('射手top.xlsx')) 120     print(ADCrank.head()) 121 
122     # #無無效列
123 
124     # 檢查是否有重復值
125     print(ADCrank.duplicated()) 126 
127     # 檢查是否有空值
128     print(ADCrank['排名'].isnull().value_counts()) 129 
130     # 異常值處理
131     print(ADCrank.describe()) 132     # 發現“排名”字段的最大值為23而平均值為12,假設異常值為23
133     # print(top2.replace([23, top2['排名'].mean()]))
134 
135     #數據可視化:
136 
137 
138     #分別導入射手數據
139     df_ADC = pd.read_excel('射手top.xlsx') 140     ADCTop = df_ADC['排名'].values.tolist() 141     ADCName = df_ADC['英雄名'].values.tolist() 142     ADCWin = df_ADC['英雄勝率'].values.tolist() 143     ADCpick = df_ADC['英雄出場率'].values.tolist() 144 
145     #分別導入上單數據
146     df_top = pd.read_excel('上單top.xlsx') 147     TOPTop = df_top['排名'].values.tolist() 148     TOPName = df_top['英雄名'].values.tolist() 149     TOPWin = df_top['英雄勝率'].values.tolist() 150     TOPpick = df_top['英雄出場率'].values.tolist() 151 
152     #利用pyecharts做表格視圖
153 
154     #繪制射手表格視圖
155     table1 = Table() 156     headers1 = ["排名", "英雄名", "英雄勝率", "英雄出場率"] #設置表頭
157     rows1 = [ 158 
159  ] 160     for i in range(0, 23): 161         rows1.append(df_ADC.iloc[i].values.tolist()[1:])    #轉換插入格式
162     table1.add(headers1, rows1)         #插入數據
163  table1.set_global_opts( 164         title_opts=ComponentTitleOpts(title="射手-強度排行", subtitle="實時更新") #設置標題與副標題
165  ) 166     table1.render("射手pyecharts表格.html") 167 
168 
169     #繪制上單表格視圖
170     table2 = Table() 171     headers2 = ["排名", "英雄名", "英雄勝率", "英雄出場率"] 172     rows2 = [ 173 
174  ] 175     for i in range(0, 56): 176         rows2.append(df_top.iloc[i].values.tolist()[1:]) #轉換插入格式
177     table2.add(headers2, rows2) #插入數據
178  table2.set_global_opts( 179         title_opts=ComponentTitleOpts(title="上單-強度排行", subtitle="實時更新") #設置標題與副標題
180  ) 181     table2.render("上單pyecharts表格.html") #記錄
182 
183     #利用pyecharts繪制柱狀圖
184 
185     #繪制射手柱狀圖
186     c = ( 187         Bar({"theme": ThemeType.MACARONS}) 188             .add_xaxis(ADCName)     #設置x軸
189             .add_yaxis("英雄勝率", ADCWin)      #添加柱狀體
190             .add_yaxis("英雄出場率", ADCpick) 191  .set_global_opts( 192             title_opts={"text": "射手強度-示意圖", "subtext": "綜合勝率與出場率"}, #設置標題與副標題
193             datazoom_opts=opts.DataZoomOpts(),  # 分段
194             xaxis_opts=opts.AxisOpts(name_rotate=60, name="英雄名", axislabel_opts={"rotate": 35})  # 字體傾斜角度
195 
196  ) 197             .render("ADC強度柱狀圖.html") 198  ) 199     #繪制上單柱狀圖
200     c = ( 201         Bar({"theme": ThemeType.MACARONS}) 202             .add_xaxis(TOPName)     #設置x軸
203             .add_yaxis("英雄勝率", TOPWin)      #添加柱狀體
204             .add_yaxis("英雄出場率", TOPpick) 205  .set_global_opts( 206             title_opts={"text": "上單強度-示意圖", "subtext": "綜合勝率與出場率"}, #設置標題與副標題
207             datazoom_opts=opts.DataZoomOpts(),  # 分段
208             xaxis_opts=opts.AxisOpts(name_rotate=60, name="英雄名", axislabel_opts={"rotate": 35})  # 字體傾斜角度
209 
210  ) 211             .render("上單強度柱狀圖.html") 212  ) 213 
214 
215     #利用pyecharts繪制折線圖:
216 
217     #繪制射手折線圖
218  ( 219  Line() 220  .set_global_opts( 221             tooltip_opts=opts.TooltipOpts(is_show=False), 222             xaxis_opts=opts.AxisOpts(type_="category"), 223             yaxis_opts=opts.AxisOpts( 224                 type_="value", 225                 axistick_opts=opts.AxisTickOpts(is_show=True), 226                 splitline_opts=opts.SplitLineOpts(is_show=True), 227  ), 228  ) 229             .add_xaxis(xaxis_data=ADCName) 230  .add_yaxis( 231             series_name="", 232             y_axis=ADCWin, 233             symbol="emptyCircle", 234             is_symbol_show=True, 235             label_opts=opts.LabelOpts(is_show=False), 236  ) 237  .set_global_opts( 238             title_opts={"text": "射手英雄-對應勝率曲線", "subtext": "強度曲線"}, 239             datazoom_opts=opts.DataZoomOpts(),  # 分段
240             xaxis_opts=opts.AxisOpts(name_rotate=60, name="英雄名", axislabel_opts={"rotate": 35})  # 字體傾斜角度
241 
242  ) 243             .render("ADC勝率折線圖.html") 244  ) 245 
246     #繪制上單折線圖
247  ( 248  Line() 249  .set_global_opts( 250             tooltip_opts=opts.TooltipOpts(is_show=False), 251             xaxis_opts=opts.AxisOpts(type_="category"), 252             yaxis_opts=opts.AxisOpts( 253                 type_="value", 254                 axistick_opts=opts.AxisTickOpts(is_show=True), 255                 splitline_opts=opts.SplitLineOpts(is_show=True), 256  ), 257  ) 258             .add_xaxis(xaxis_data=TOPName) 259  .add_yaxis( 260             series_name="", 261             y_axis=TOPWin, 262             symbol="emptyCircle", 263             is_symbol_show=True, 264             label_opts=opts.LabelOpts(is_show=False), 265  ) 266  .set_global_opts( 267             title_opts={"text": "上單英雄-對應勝率曲線", "subtext": "強度曲線"}, 268             datazoom_opts=opts.DataZoomOpts(),  # 分段
269             xaxis_opts=opts.AxisOpts(name_rotate=60, name="英雄名", axislabel_opts={"rotate": 35})  # 字體傾斜角度
270 
271  ) 272             .render("上單勝率折線圖.html") 273  ) 274 
275 
276 
277 
278 
279 #調用主函數
280 if __name__=="__main__": 281  main() 282     print('程序運行成功')

五、總結

1.經過對主題數據的分析與可視化,可以得到哪些結論?是否達到預期的目標?

結論:

   (1)游戲里英雄強度強,並不代表着高選取率高勝率的結果。

           (2)獲取和處理數據上我鞏固了許多知識,使運用他們變得更加熟悉。

達到了預期的目標。

2.在完成此設計過程中,得到哪些收獲?以及要改進的建議?

收獲:熟練了爬蟲的操作,且能更好的運用正則表達式

建議:有很多小細節卡了我很久,還是需要穩固基礎。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM