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