手动反爬虫,禁止转载:原博地址 https://blog.csdn.net/lys_828/article/details/122135788(CSDN博主:Be_melting)

 知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息

项目7:利用 Dash实现宝可梦能力图表和可视化雷达图

7.1 项目完成功能预览

项目完成后的预览界面如下。页面分为上下两个部分,上面是展示两列,左侧一列放置宝可梦的图片和介绍信息,右侧一例放置宝可梦能力的可视化雷达图;下面的所有的宝可梦能力图表,通过点击前面的单选框,可以进行宝可梦的信息查看。
在这里插入图片描述
比如鼠标点击第二个,输出的结果如下。
在这里插入图片描述
此外表中第二行是一个筛选行,可以针对任何一个字段或者多个字段进行数据筛选。比如筛选火属性并且攻击值大于100的宝可梦。
在这里插入图片描述
接下来就可以把任务拆解,上下两个部分,先完成下面的只有一个表格的设置,然后在整上面的有两个图片的设置。

7.2 数据表格设置

[1] 如果使用dash_bootstrap_components里面的Table进行绘制,就需要通过table_header + table_body进行合并绘制,代码可以参考官方网址:http://dash-bootstrap-components.opensource.faculty.ai/docs/components/table/。
请添加图片描述
[2] 上面方式比较麻烦,推荐比较好用的就是dash_table模块,需要独立进行安装(命令行打开后pip install dash_table),可以和dash进行交互,官方文档:https://dash.plotly.com/datatable。比如按照官网的示例,几行代码就可以快速的生成一个表格。

import dash
import dash_table
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')

app = dash.Dash(__name__)

app.layout = dash_table.DataTable(
    id='table',
    columns=[{"name": i, "id": i} for i in df.columns],
    data=df.to_dict('records'),
)

if __name__ == '__main__':
    app.run_server(debug=True)

新建一个py文件,粘贴上述代码后,保存运行后,刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
而solar.csv文件中的内容如下。上述中间的三行代码实现了数据从csv文件展示在网页中。
请添加图片描述
[3] 按照类似的操作,只需要把文件从本地读入后替换即可,尝试一下,只需要修改读取文件的路径。

df = pd.read_csv(r'E:\lesson\lesson07\pokemon.csv')

刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
[4] 丰富表格中的设置,比如每页显示的数据条数,字段筛选,第一列的单选按钮操作。比如每页显示10条数据,只需要添加一个page_size参数,指定为10;字段筛选控制参数为filter_action=‘native’(可用于筛选1w-10w的数据量);单选按钮是通过row_selectable='single’控制,赋值可以为multi表示多选。代码操作如下。

app.layout = dash_table.DataTable(
    id='table',
    columns=[{"name": i, "id": i} for i in df.columns],
    data=df.to_dict('records'),
    page_size=10,
    row_selectable='single',
    filter_action='native'
)

保存py文件后,刷新网址:http://127.0.0.1:8050/,输出结果如下。至此就完成了表格数据的呈现,满足最初预览的功能。
请添加图片描述

7.3 可视化雷达图的绘制

[1] 图形模板查找。这里就用到了之前项目4中内容,dash与plotly进行结合绘制可视化图表,所有的可视化的图形都在:https://plotly.com/python/网址下可以找到,学会利用搜索框,比如绘制的雷达图可以直接在搜索框中搜索radar,然后找到对应的结果。该图形就是在 Scientific Charts类图表中:https://plotly.com/python/scientific-charts,如果在搜索框中没有找到对应的图形,就需要在各个分类中进行图形的查找。
请添加图片描述
[2] 应用图形模板。第一个搜索结果点进去后,界面中打开找到的图形绘制的示例代码,如下。仔细看看该网页中示例的图形代码,有使用go也有使用px进行绘制的,对比而言,使用px要简洁许多。
请添加图片描述
将代码复制粘贴到py文件中,其中关于绘制line_polar图形中,还有一些参数可以使用,比如这里的markers就是给每个方向的点做标记,start_angle表示绘制是图形开始的角度,这里指定为0就是为了让中间的刻度线与一个方向的线重合,图形更和谐一些。

import plotly.express as px
import pandas as pd
df = pd.DataFrame(dict(
    r=[1, 5, 2, 2, 3],
    theta=['processing cost','mechanical properties','chemical stability',
           'thermal stability', 'device integration']))
fig = px.line_polar(df, r='r', theta='theta', line_close=True,markers='.',start_angle=0)
fig.update_traces(fill='toself')
fig.show()

运行输出结果如下。也可以根据自己的需求进行参数的调节,此外还有其它的一些参数属性,可以调出说明文档进行查看。
请添加图片描述
[3] 将绘制的雷达图与dash进行结合。用到项目4中的内容,完整代码如下。

import plotly.express as px
import pandas as pd
import dash
from dash import dcc
from dash import html

df = pd.DataFrame(dict(
    r=[1, 5, 2, 2, 3],
    theta=['processing cost','mechanical properties','chemical stability',
           'thermal stability', 'device integration']
    )
)
fig = px.line_polar(df, r='r', theta='theta', line_close=True,markers='.',start_angle=0)
fig.update_traces(fill='toself')

app = dash.Dash()
app.layout = html.Div([dcc.Graph(figure=fig)])
app.run_server(debug=True)

保存py文件后,刷新网址:http://127.0.0.1:8050/,输出结果如下。至此关于雷达图与dash交互工作就完成。
请添加图片描述

7.4 宝可梦信息交互设置

最后一部分的设置就是上方左侧,关于宝可梦信息的设置。首先就是呈现的图片,由于此次项目中数据量相对较多,如果采用根据名称去搜索引擎搜索,然后再把图片下载到本地进行加载,过程太消耗时间,不太可取。可以尝试寻找有没有专门收藏宝可梦的网站,里面应该会有详细的资料介绍。提供的数据集中,宝可梦的信息较少,尝试重新收集最新数据。

[1] 数据采集。尝试一番,可以参考网址:https://pokemondb.net/pokedex/all,网址界面如下。
请添加图片描述
可以直接进行网络爬虫获取里面的数据,代码如下。

import requests
import pandas as pd
from bs4 import BeautifulSoup

url = 'https://pokemondb.net/pokedex/all'
html = requests.get(url)
soup = BeautifulSoup(html.text,'lxml')
infos = soup.select('#pokedex tbody tr')
data_ls = []
for info in infos:
    dic ={}
    if info.select_one('.text-muted'):
        name = info.select_one('.text-muted').text
    else:
        name = get_text('.ent-name')
    num = get_text('.infocard-cell-data')
    type = get_text('.cell-icon')
    total = get_text('.cell-total')
    [HP,Attack,Defense,Sp_Atk,Sp_Def,Speed] = info.select('.cell-num')[-6:]
    if ' ' in name:
        #需要注意宝可梦名称有3个单词时候怎么处理,这里留一个空白供思考,当前没有处理这个情况,有三个单词名称的宝可梦很少
        # img_url = 'https://img.pokemondb.net/artwork/large/' + name.replace(' ','-').lower() + '.jpg'
        img_url = 'https://img.pokemondb.net/artwork/large/' + '-'.join(name.split(' ')[::-1]).lower() + '.jpg'
    else:
        img_url = 'https://img.pokemondb.net/artwork/large/' + name.lower() + '.jpg'
    dic['name'] = name
    dic['num'] = num 
    dic['type'] = type 
    dic['total'] = total
    dic['HP'] = HP.text 
    dic['Attack'] = Attack.text
    dic['Defense'] = Defense.text
    dic['Sp_Atk'] = Sp_Atk.text 
    dic['Sp_Def'] = Sp_Def.text
    dic['Speed'] = Speed.text
    dic['img_url'] =  img_url
    data_ls.append(dic)

df = pd.DataFrame(data_ls)
df.to_csv('pokemon_new.csv',index=False)

输出结果如下。
请添加图片描述
如果对于英文有点陌生,也可以参照中文版的网站:https://www.pokemon.cn/play/pokedex。
请添加图片描述

[2] 数据交互。数据采集完毕后,进行网页数据的展示,比如选取第一行的数据进行展示,代码如下。

import dash
from dash import html
from dash import dcc
import pandas as pd

df = pd.read_csv(r'E:\lesson\lesson07\pokemon_new.csv')
print(df.name[0])

app = dash.Dash()

app.layout = html.Div([
    html.Img(src = df.img_url[0]),
    html.H1(str(df.num[0])+df.name[0]),
    html.H3(df.type[0])
])

app.run_server()

保存py文件后,刷新网址:http://127.0.0.1:8050/,输出结果如下。至此所有页面上需要呈现的元素均已准备好
请添加图片描述

7.5 完善输入输出流及布局优化

[1] 网格系统布局。由展示的预期效果可以看出,整个网页的排版是由上下两个部分构成,上面部分又被分割为左右部分。在dash中可以实现网格布局的就是layout组件,官方网址:http://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/。
请添加图片描述
新建一个py文件,进行示例代码操作。

import dash_bootstrap_components as dbc
from dash import html

row = html.Div(
    [
        dbc.Row(dbc.Col(html.Div("A single column"))),
        dbc.Row(
            [
                dbc.Col(html.Div("One of three columns")),
                dbc.Col(html.Div("One of three columns")),
                dbc.Col(html.Div("One of three columns")),
            ]
        ),
    ]
)

保存文件后,刷新网址:http://127.0.0.1:8050/,输出结果如下。注意这里要把之前项目6里面assets文件夹复制过来,需要自动加载里面的boostrap中的css样式。
请添加图片描述
如果进一步的明显的观察,可以设置一下元素居中,需要使用到style参数,修改代码及网站刷新输出结果如下。
请添加图片描述
[2] 表格点击数据交互。网格布局内容介绍完毕后,接着就是要实现下方表格前面的单选按钮点击后需要将结果反馈到上方,导入dash中的数据输出输出流。

import dash 
from dash import dcc
from dash import html
import pandas as pd
from dash.dependencies import Output,Input
import dash_bootstrap_components as dbc
import dash_table

df = pd.read_csv(r'E:\lesson\lesson07\pokemon_new.csv')

app = dash.Dash()
table = dash_table.DataTable(
    id = 'table',
    columns=[{'name':i,'id':i} for i in df.columns],
    data = df.to_dict('records'),
    page_size=10,
    filter_action = 'native',
    row_selectable = 'single'
)

app.layout = html.Div([
    #这里用两个Container来填充上下部分
    dbc.Container('hi',id = 'target',style={'backgroundColor':'white'},className="shadow"),
    dbc.Container([table],className='shadow mt-3')
])

@app.callback(
    Output('target','children'),  	  #返回到id为target对应为children属性
    [Input('table','selected_rows')]  #传入的是id为table对应为selected_rows的属性
)
def rule(selected_rows):
    return selected_rows  #直接原路返回输出结果

app.run_server(debug=True)

保存文件后,刷新网址:http://127.0.0.1:8050/,输出结果如下。注意上方输出的结果,点击第一行数据前面的单选按钮,输出的是0。
请添加图片描述

然后再选其它行前面的单选按钮,输出结果如下。比如选择的是第6行的数据,上方输出的就是5,由此可以总结,这里选择的输入组件的属性就是鼠标点击后返回数据所在的行数减1。
请添加图片描述

根据行编号获取数据,利用pandas的行索引功能特别简单,比如直接获取第一行的数据,df.loc[0].to_list()即可,测试输出结果如下。
请添加图片描述
但是需要注意,真实返回值的输出结果中,应当留意,可以参照项目6制作的汇率计算工具,这里也会遇到相同的问题,加上一个print输出,结果如下。刚刷新后自动返回的是None值,然后点击第一行的单选按钮后就返回的是一个列表0,并不是整型的数值0。
请添加图片描述
因此需要处理两个地方,整理后的的代码如下。

def rule(selected_rows):
    # print(selected_rows)
    selected_rows = 0 if not selected_rows else selected_rows[0]
    return df.loc[selected_rows].to_list()

保存文件后,再次刷新网址:http://127.0.0.1:8050/,输出结果如下。上方的结果中把第一行的内容显示出来了,但是对比前面的测试输出,可以发现网页中自动把数据之间的逗号给省略了。
请添加图片描述
[3] 分配数据。更进一步就可以给上面部分的内容分配数据,前四列和最后一列的数据是绘制左边的信息,中间的六列是绘制右侧的信息。

def rule(selected_rows):
    # print(selected_rows)
    selected_rows = 0 if not selected_rows else selected_rows[0]
    pokemon_data = df.loc[selected_rows].to_list()
    name = pokemon_data[0]
    tags = pokemon_data[1:4]
    abls = pokemon_data[4:10]
    img = pokemon_data[-1]
    return f'name:{name}, tags:{tags}, abls:{abls}, img:{img}'

保存文件后,再次刷新网址:http://127.0.0.1:8050/,输出结果如下。上方的结果证明分配数据无误。
请添加图片描述
[4] 信息联动。将分配好的数据分别放置在上方的左右两侧,并进行可视化表达。为了主程序的代码的简洁性,建议重新创建一个py文件,单独处理布局和绘制图形的部分,最后返回的就是一个html布局。

import dash
from dash import html
from dash import dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input,Output
from dash import dash_table
import pandas as pd
import plotly.express as px

def JumboItem(name,tags,abls,img):
    hex_area = {
        "r":abls,
        "theta": ["HP", "Attack", "Defense", "Sp.Attack", "Sp.Defense", "Speed"],
    }
    fig = px.line_polar(
        hex_area, 
        r='r', 
        theta='theta', 
        line_close=True,
        range_r=[0,255],
        width=300,
        height=300,
    )
    fig.update_traces(fill="toself")
    return dbc.Row(
        dbc.Col(
            [
                html.Img(src=img),
                html.H1(name.upper()),
                html.Span([i for i in tags])
            ]
        ),
        dbc.Col(
            [
                dcc.Graph(figure=fig)
            ]
        )
    )

保存文件后,在主程序中引入这个模块,然后修改return值。

from jumbo_item import JumboItem

def rule(selected_rows):
    # print(selected_rows)
    selected_rows = 0 if not selected_rows else selected_rows[0]
    pokemon_data = df.loc[selected_rows].to_list()
    name = pokemon_data[0]
    tags = pokemon_data[1:4]
    abls = pokemon_data[4:10]
    img = pokemon_data[-1]
    return JumboItem(name,tags,abls,img)

重新运行py文件,再次刷新网址:http://127.0.0.1:8050/,输出结果如下。所有的元素都呈现到网页上了,接下来就是进行元素大小和布局的设置。
请添加图片描述
[5] 网页布局优化。设置图片和雷达图的宽和高,然后给属性加上一个样式,最后元素都进行居中。

import dash
from dash import html
from dash import dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input,Output
from dash import dash_table
import pandas as pd
import plotly.express as px

def JumboItem(name,tags,abls,img):
    hex_area = {
        "r":abls,
        "theta": ["HP", "Attack", "Defense", "Sp.Attack", "Sp.Defense", "Speed"],
    }
    fig = px.line_polar(
        hex_area, 
        r='r', 
        theta='theta', 
        line_close=True,
        range_r=[0,255],
        markers='.',   #突出交点
        start_angle=0, #开始角度
        width=400,     #宽设置为400
        height=400,	   #高设置为400
    )
    fig.update_traces(fill="toself")
    return dbc.Row([
        dbc.Col(
            [
                html.Img(src=img,
                        height="300px",  #高设置300
                        width="300px",   #宽设置300
                        ),
                html.H1(name.upper()),
                #添加一个背景底色
                html.Span([dbc.Badge(i, color="primary", className="mr-1") for i in tags])   
            ]
        ),
        dbc.Col(
            [
                dcc.Graph(figure=fig)
            ]
        ) 
    ],style={'textAlign':'center'} #进行居中处理
    )

重新运行py文件,再次刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
至此整个项目7,利用 Dash实现神奇宝贝能力图表和可视化雷达图全部过程就介绍完毕。核心知识点包含:

(1) 网格布局设计;

(2) 数据表格呈现与数据联动;

(3) 数据源获取与数据分配;

(4) 数据流入/流出设计;

(5) 页面元素布局优化。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐