“整篇文章较长,干货很多!建议收藏后,分章节阅读。”
整体设计方案思维导图:
整篇文章,也将按照这个结构来讲解。
若有重点关注部分,可点击章节目录直接跳转!
针对TOP250排行榜的数据,开发一套可视化数据大屏系统,展示各维度数据分析结果。
TOP250排行榜
import requests # 发送请求from bs4 import BeautifulSoup # 解析网页import pandas as pd # 存取csvfrom time import sleep # 等待时间from sqlalchemy import create_engine # 连接数据库定义一些空列表,用于临时存储爬取下的数据:
movie_name = [] # 电影名称movie_url = [] # 电影链接movie_star = [] # 电影评分movie_star_people = [] # 评分人数movie_director = [] # 导演movie_actor = [] # 主演movie_year = [] # 上映年份movie_country = [] # 国家movie_type = [] # 类型short_comment = [] # 一句话短评向网页发送请求:
res = requests.get(url, headers=headers)利用BeautifulSoup库解析响应页面:
soup = BeautifulSoup(res.text, 'html.parser')用BeautifulSoup的select函数,(css解析的方法)编写代码逻辑,部分核心代码:
for movie in soup.select('.item'): name = movie.select('.hd a')[0].text.replace('\n', '') # 电影名称 movie_name.append(name) url = movie.select('.hd a')[0]['href'] # 电影链接 movie_url.append(url) star = movie.select('.rating_num')[0].text # 电影评分 movie_star.append(star) star_people = movie.select('.star span')[3].text # 评分人数 star_people = star_people.strip().replace('人评价', '')其中,需要说明的是,《大闹天宫》这部电影和其他电影页面排版不同:
所以,这里特殊处理一下:
if name == '大闹天宫 / 大闹天宫 上下集 / The Monkey King': # 大闹天宫,特殊处理 year0 = movie_infos.split('\n')[1].split('/')[0].strip() year1 = movie_infos.split('\n')[1].split('/')[1].strip() year2 = movie_infos.split('\n')[1].split('/')[2].strip() year = year0 + '/' + year1 + '/' + year2 movie_year.append(year) country = movie_infos.split('\n')[1].split('/')[3].strip() movie_country.append(country) type = movie_infos.split('\n')[1].split('/')[4].strip() movie_type.append(type)最后,将爬取到的数据保存到csv文件中:
def save_to_csv(csv_name): """ 数据保存到csv :return: None """ df = pd.DataFrame() # 初始化一个DataFrame对象 df['电影名称'] = movie_name df['电影链接'] = movie_url df['电影评分'] = movie_star df['评分人数'] = movie_star_people df['导演'] = movie_director df['主演'] = movie_actor df['上映年份'] = movie_year df['国家'] = movie_country df['类型'] = movie_type df.to_csv(csv_name, encoding='utf_8_sig') # 将数据保存到csv文件其中,把各个list赋值为DataFrame的各个列,就把list数据转换为了DataFrame数据,然后直接to_csv保存。
同步讲解视频:
https://www.zhihu.com/zvideo/1465578220191592448
然后,就可以把csv数据导入到MySQL数据库,做持久化存储了。
import pandas as pd # 存取csvfrom sqlalchemy import create_engine # 连接数据库最核心的三行代码:
# 把csv导入mysql数据库engine = create_engine('mysql+pymysql://root:123456@localhost/db_bigscreen')df = pd.read_csv('Movie250.csv')df.to_sql(name='t_film', con=engine, chunksize=1000, if_exists='replace', index=None)用create_engine创建数据库连接,格式为:
create_engine('数据库类型+数据库驱动://用户名:密码@数据库IP地址/数据库名称')
这样,数据库连接就创建好了。
然后,用pandas的read_csv函数读取csv文件。
最后,用pandas的to_sql函数,把数据存入MySQL数据库:
name='college_t2' #mysql数据库中的表名
con=engine # 数据库连接
index=False #不包含索引字段
if_exists='replace' #如果表中存在数据,就替换掉,另外,还支持append(追加数据)
非常方便地完成了反向导入,即:从csv向数据库的导入。
同步讲解视频:
https://www.zhihu.com/zvideo/1496218294043009024
如文章开头的思维导图所说,首先把各个子图表开发出来,然后用pyecharts的Page组件,把这些子图表拼装组合起来,形成大屏。
下面,依次讲解每个子图表的实现。
pyecharts官网-柱形图:A Python Echarts Plotting Library built with love.
因为需要实现分段区间统计,所以先定义出一个区间对象:
# 设置分段bins = [0, 100000, 200000, 300000, 500000, 1000000, 3000000]# 设置标签labels = ['0-10w', '10w-20w', '20w-30w', '30w-50w', '50w-100w', '100w-300w']然后,对数据进行按段切割,并统计个数:
# 按分段离散化数据segments = pd.cut(cmt_count_list, bins, labels=labels) # 按分段切割数据counts = pd.value_counts(segments, sort=False).values.tolist() # 统计个数最后,采用pyecharts里的Bar对象,画出柱形图:
bar = Bar( init_opts=opts.InitOpts(theme=theme_config, width="450px", height="350px", chart_id='bar_cmt2')) # 初始化条形图bar.add_xaxis(labels, ) # 增加x轴数据bar.add_yaxis("评价数", counts) # 增加y轴数据bar.set_global_opts( legend_opts=opts.LegendOpts(pos_left='right'), title_opts=opts.TitleOpts(title="评价数量区间分布-柱形图", pos_left='center'), # 标题 toolbox_opts=opts.ToolboxOpts(is_show=False, ), # 不显示工具箱 xaxis_opts=opts.AxisOpts(name="评论数", # x轴名称 axislabel_opts=opts.LabelOpts(font_size=8)), # 字体大小 yaxis_opts=opts.AxisOpts(name="电影数量", axislabel_opts={"rotate": 0}, splitline_opts=opts.SplitLineOpts(is_show=True, linestyle_opts=opts.LineStyleOpts(type_='solid')), ), # y轴名称)# 标记最大值bar.set_series_opts( markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="最大值"), ], symbol_size=35) # 标记符号大小)bar.render("评价数分布-柱形图.html") # 生成html文件print('生成完毕:评价数分布-柱形图.html')图表效果:
pyecharts官网-饼图:A Python Echarts Plotting Library built with love.
绘制情感分布的饼图。所以,首先要对评价数据进行情感分析。
鉴于电影评价内容都是中文文本设计,情感分析采用snownlp技术进行。
score_list = [] # 情感评分值tag_list = [] # 打标分类结果pos_count = 0 # 计数器-积极mid_count = 0 # 计数器-中性neg_count = 0 # 计数器-消极for comment in v_cmt_list: tag = '' sentiments_score = SnowNLP(comment).sentiments if sentiments_score < 0.4: # 情感分小于0.4判定为消极 tag = '消极' neg_count += 1 elif 0.4 <= sentiments_score <= 0.6: # 情感分在[0.4,0.6]直接判定为中性 tag = '中性' mid_count += 1 else: # 情感分大于0.6判定为积极 tag = '积极' pos_count += 1 score_list.append(sentiments_score) # 得分值 tag_list.append(tag) # 判定结果df['情感得分'] = score_listdf['分析结果'] = tag_listdf.to_excel('情感判定结果.xlsx', index=None) # 把情感分析结果保存到excel文件按照情感得分值划分区间:
情感得分值小于0.4,判定为消极
情感得分值在0.4与0.6之间,判定为中性
情感得分值大于0.6,判定为积极
最终将结果保存到Excel文件中,查看下:
将此结果中的数据,带入到Pie组件中,画出饼图:
# 画饼图pie = ( Pie(init_opts=opts.InitOpts(theme=theme_config, width="450px", height="350px", chart_id='pie1')) .add(series_name="评价情感分布", # 系列名称 data_pair=[['积极', pos_count], # 添加数据 ['中性', mid_count], ['消极', neg_count]], rosetype="radius", # 是否展示成南丁格尔图 radius=["30%", "55%"], # 扇区圆心角展现数据的百分比,半径展现数据的大小 ) # 加入数据 .set_global_opts( # 全局设置项 title_opts=opts.TitleOpts(title="短评情感分布-饼图", pos_left='center'), # 标题 legend_opts=opts.LegendOpts(pos_left='right', orient='vertical') # 图例设置项,靠右,竖向排列 ) .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))) # 样式设置项pie.render('情感分布_饼图.html') # 生成html文件print('生成完毕:情感分布_饼图.html')图表效果:
pyecharts官网-词云图:A Python Echarts Plotting Library built with love.
针对TOP250的电影名称,绘制出词云图。
先对数据做清洗操作,然后直接画出词云图即可:
wc = WordCloud(init_opts=opts.InitOpts(width="450px", height="350px", theme=theme_config, chart_id='wc1'))wc.add(series_name="电影名称", data_pair=data, word_size_range=[15, 20], width='400px', # 宽度 height='300px', # 高度 word_gap=5 # 单词间隔 ) # 增加数据wc.set_global_opts( title_opts=opts.TitleOpts(pos_left='center', title="电影名称分析-词云图", title_textstyle_opts=opts.TextStyleOpts(font_size=20) # 设置标题 ), tooltip_opts=opts.TooltipOpts(is_show=True), # 不显示工具箱)wc.set_series_opts(label_opts=opts.LabelOpts(is_show=True))wc.render('电影名称_词云图.html') # 生成html文件print('生成完毕:电影名称_词云图.html')图表效果:
pyecharts官网-表格:A Python Echarts Plotting Library built with love.
把排名前10的电影详情数据,展现到大屏上,采用pyecharts里的Table组件实现。
从MySQL数据库读取到数据后,直接进行绘制表格:
table = ( Table(page_title='我的表格标题', ) .add(headers=['排名', '电影名称', '评分', '评论数', '上映年', '一句话短评'], rows=data_list, attributes={ "align": "left", "border": False, "padding": "20px", "style": "background:{}; width:450px; height:350px; font-size:10px; color:#C0C0C0;padding:3px;".format( table_color) }) .set_global_opts(title_opts=opts.TitleOpts(title='这是表格1')))table.render('电影排名TOP10_数据表格.html')print('生成完毕:电影排名TOP10_数据表格.html')图表效果:
pyecharts官网-涟漪散点图:A Python Echarts Plotting Library built with love.
针对电影的上映年份和评分值,两个纬度的数据,绘制出涟漪散点图(涟漪散点图和普通散点图的区别,就是涟漪散点图是动态图,图上的每个点都在闪烁,像水面上的涟漪一样)。
sc = (EffectScatter(init_opts=opts.InitOpts(width="450px", height="350px", theme=theme_config, chart_id='scatter1')) .add_xaxis(xaxis_data=x_data) .add_yaxis( series_name="", y_axis=y_data, symbol_size=10, label_opts=opts.LabelOpts(is_show=False),) .set_series_opts() .set_global_opts( # 忽略部分代码 ) )sc.render('评分年份分布-散点图.html')print('生成完毕:散点图.html')图表效果:
pyecharts官网-条形图:A Python Echarts Plotting Library built with love.
针对评论数最多的10个电影名称,绘制出横向条形图。
# 画条形图bar = Bar( init_opts=opts.InitOpts(theme=theme_config, width="450px", height="350px", chart_id='bar_cmt1')) # 初始化条形图bar.add_xaxis(x_data) # 增加x轴数据bar.add_yaxis("评论数量", y_data) # 增加y轴数据bar.reversal_axis() # 设置水平方向bar.set_series_opts(label_opts=opts.LabelOpts(position="right")) # Label出现位置bar.set_global_opts( legend_opts=opts.LegendOpts(pos_left='right'), title_opts=opts.TitleOpts(title="评论数TOP10作者-条形图", pos_left='center'), # 标题 toolbox_opts=opts.ToolboxOpts(is_show=False, ), # 不显示工具箱 xaxis_opts=opts.AxisOpts(name="评论", # x轴名称 axislabel_opts=opts.LabelOpts(font_size=8, rotate=0), splitline_opts=opts.SplitLineOpts(is_show=False) ), yaxis_opts=opts.AxisOpts(name="电影", # y轴名称 axislabel_opts=opts.LabelOpts(font_size=7, rotate=45), # y轴名称 ))bar.render("评论数TOP10_条形图.html") # 生成html文件print('生成完毕:评论数TOP10_条形图.html')图表效果:
由于pyecharts组件没有专门用作标题的图表,我决定灵活运用Table组件实现大标题。即,让Table只有标题header,没有数据行row,再针对header做一些样式调整(字体增大等),即可实现一行大标题。
table = Table()table.add(headers=[v_title], rows=[], attributes={ "align": "center", "border": False, "padding": "2px", "style": "background:{}; width:1350px; height:50px; font-size:25px; color:#C0C0C0;".format(table_color)})table.render('大标题.html')print('生成完毕:大标题.html')图表效果:
最后,也是最关键的一步,把以上所有图表组合到一起,用Page组件,并且选用DraggablePageLayout方法,即拖拽的方式,组合图表:
# 绘制:整个页面page = Page( page_title="基于Python的电影数据分析大屏", layout=Page.DraggablePageLayout, # 拖拽方式)page.add( # 增加:大标题 make_title(v_title='基于Python的电影数据分析大屏'), # 绘制:中下方数据表格 make_table(v_df=df_table), # 绘制:电影名称词云图 filmname_wordcloud(v_str=film_all_list), # 绘制:TOP10评论数-条形图 make_top10_comment_bar(v_df=df), # 绘制情感分布饼图 make_analyse_pie(v_cmt_list=comment_all_list), # 绘制:评价数分段统计-柱形图 make_cmt_count_bar(v_df=df), # 绘制:散点图 make_scatter(x_data=year_list, y_data=score_list))page.render('大屏_临时.html') # 执行完毕后,打开临时html并排版,排版完点击Save Config,把json文件放到本目录下print('生成完毕:大屏_临时.html')本代码执行完毕后,打开临时html并排版,排版完点击SaveConfig,把json文件放到本目录下。
再执行最后一步,调用json配置文件,生成最终大屏文件。
# 执行之前,请确保:1、已经把json文件放到本目录下 2、把json中的title和table的id替换掉Page.save_resize_html( source="大屏_临时.html", cfg_file="chart_config.json", dest="大屏_最终_0426.html")拖拽过程的演示视频:
https://www.zhihu.com/zvideo/1502249430140616704
至此,所有代码执行完毕,生成了最终大屏html文件。
为了实现不同颜色主题的大屏可视化效果,我开发了一个实现逻辑,只需修改一个参数,即可展示不同颜色主题。
theme_config = ThemeType.CHALK # 颜色方案
由于Table组件是不能设置颜色主题的,所以我手写了一个逻辑(用取色器获取的RGB值,又转成十六进制的颜色!),如下:
# 表格和标题的颜色table_color = ""if theme_config == ThemeType.DARK: table_color = '#333333'elif theme_config == ThemeType.CHALK: table_color = '#293441'elif theme_config == ThemeType.PURPLE_PASSION: table_color = '#5B5C6E'elif theme_config == ThemeType.ROMANTIC: table_color = '#F0E8CD'elif theme_config == ThemeType.ESSOS: table_color = '#FDFCF5'else: table_color = ''最终实现了多种颜色主题,包含以下。





通过5种主题颜色,展示同一个大屏效果,有被炫到嘛?
拖拽过程演示视频:
https://www.zhihu.com/zvideo/1502249430140616704
全流程讲解:
https://www.zhihu.com/zvideo/1503013679826690048
by 马哥python说