news 2026/10/5 11:03:13

Python+Flask+ECharts京东手机数据可视化分析实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python+Flask+ECharts京东手机数据可视化分析实战

简介:基于Python与ECharts的京东手机销售数据分析与可视化技术论文PDF,面向电商从业者、数据分析人员及计算机专业学生与研究人员。论文完整介绍从京东官网爬取手机销售数据(含参数与用户评价),利用Pandas进行清洗去重,再迁移至MySQL存储,并通过Flask+ECharts构建可视化界面的系统实现过程。资源为单个PDF文档,共2.01MB,包含摘要、系统框架、功能模块设计、数据清洗与可视化流程等核心章节。目前已有265人学习,可用于电商平台选品分析、消费者购机决策参考和企业营销策略制定等场景,适合需要快速了解电商数据采集、清洗、存储与可视化闭环的读者参考。通过阅读,可以掌握爬虫应对反爬机制的策略、Pandas常用数据处理函数、数据库迁移方案,以及前端图表与后端Flask联动的具体做法,其中对反爬细节与数据清洗步骤的说明尤其具备实践参考价值,对独立搭建类似数据分析项目有直接帮助。

1. 京东手机销售数据分析:一套能跑通的 Python+Flask+ECharts 完整项目

做电商数据分析的人,手里最缺的不是算法,而是一套“从数据到图表”能完整跑通的样板。这个基于 Python 和 ECharts 的京东手机销售数据分析与可视化项目,正好把这条链路补全了:爬虫抓京东手机参数和用户评价,Pandas 清洗,MySQL 存储,最后用 Flask+ECharts 拼出一个可视化大屏。它不是零散的代码片段,而是一个带登录、带图表联动、带词频统计的完整系统。适合三类人:刚学完 Python 基础想练手的学生,需要做电商数据报表的运营,以及想快速搭建内部数据看板的开发。我拆完这套资源后最大的感受是:技术栈不新,但胜在链路完整,尤其翻页 URL 规律的挖掘和异常数据的清洗规则,都是可以直接抄作业的硬通货。

2. 系统框架与数据流:从京东页面到可视化大屏的完整管线

2.1 三段式架构:爬虫独立、清洗独立、展示独立

整个系统分成三个松耦合模块:数据爬虫模块、数据清洗模块和数据可视化模块。这种拆分不是拍脑袋定的,而是考虑到三个环节的失败模式完全不同——爬虫可能被封 IP,清洗可能遇到脏数据,可视化可能遇到图表不渲染。三者独立后,哪个环节出问题就单独排查哪个,不至于牵一发动全身。

数据流向是单向的:爬虫把京东手机详情页的数据写入 CSV 文件作为过渡存储,Pandas 读取 CSV 做清洗和格式化,清洗后的数据通过 pymysql 迁移进 MySQL 的三张表,最后 Flask 后端从 MySQL 查询数据并转成 JSON 返回给前端,前端页面里的 ECharts 拿到 JSON 数据渲染图表。这套流程里 CSV 其实是个“后悔药”设计——清洗脚本跑挂了,或者清洗规则改动了,直接从 CSV 重新跑就行,不用重新爬一遍网站,这个习惯值得保留。

2.2 MySQL 表设计:四张表撑起整个业务

系统设计了四张表:手机详情信息表(p_info)、用户评价标签表(p_tag)、用户评论内容表(p_comment)和用户注册表(user)。前两张表的结构设计得比较典型,直接决定后续统计 SQL 好不好写。p_info 表的核心字段包括 id、brand、shop、title、os、价格、上市年份、上市月份、机身重量、厚度、长度、摄像头数量等 30 个参数;p_tag 表则包含 id、brand、shop、title、tag 和 num,其中 tag 存评价标签文本,num 存该标签的统计数量。这种设计的好处是:品牌维度统计直接 group by brand,评价标签的词频直接 sum(num),不需要在查询时做复杂的字符串解析。

MySQL 连接串是标准写法,注意 charset 必须指定 utf8,否则中文写入会变乱码。连接参数建议单独维护在一个配置文件中,不要写死在业务代码里,后面换库或者调连接池都方便。

2.3 Flask+ECharts 的分工边界

Flask 在这里只做两件事:提供登录注册接口,以及把 MySQL 查询结果转成 JSON 返回给前端。图表渲染完全交给 ECharts 在前端完成,这种分工能减少后端压力,也让图表交互(比如 tooltip 悬停、图例筛选)不依赖网络请求。前端使用 HTML、CSS、JavaScript 和 JQuery,通过 JQuery 的 ajax 从 Flask 接口拉数据,拿到数据后再用 ECharts 的 setOption 填充图表。

有个细节值得注意:Flask 返回 JSON 时,如果数据是 numpy 类型(比如用 pandas 的 groupby 算出来的结果),直接 jsonify 会报错。需要先用 tolist() 或 astype 转成 Python 原生类型,这是最常见的翻车点。

3. 爬虫实现细节:京东翻页 URL 规律与反爬应对

3.1 前 30 条和后 30 条商品的不同请求方式

京东手机展示页的 URL 规则是这套系统的核心资产。每一页的商品展示分为前 30 条和后 30 条,前 30 条的请求地址是 list.html,后 30 条是 listNew.php,两者的翻页参数规则完全不同。前 30 条的 URL 结构为:

url = 'https://list.jd.com/list.html?cat=9987%2C653%2C655&ev=exbrand_' + brand + '&page=' + str(i) + '&s=' + str(j) + '&click=0'

其中 brand 是品牌名称,i 是页码(步长为 2),j 是偏移量(步长为 60)。为什么 i 的步长是 2?因为京东列表页每翻一页实际加载两页的内容,第一次请求 page=1、s=1,第二次请求 page=3、s=61,以此类推。s 参数决定从第几条记录开始展示,步长 60 正好对应每页 30 条商品的两倍。如果只改 page 不改 s,你会拿到重复的数据。

后 30 条的结构稍有不同:

url1 = 'https://list.jd.com/listNew.php?cat=9987%2C653%2C655&ev=exbrand_' + t + '&page=' + str(k) + '&s=' + str(z) + '&scrolling=y'

这里 k 取 range(2, 4, 2) 即值为 2,z 取 range(27, 87, 60) 即值为 27。也就是说后 30 条的 page 从 2 开始,s 从 27 开始。循环用 zip 函数把两个序列配对,代码更简洁。cat 参数 9987%2C653%2C655 是京东手机品类的分类 ID,这个值是从京东页面源码里挖出来的,不同品类这个值不一样,如果要爬别的品类需要重新抓取。

3.2 代理 IP 和睡眠时间:应对反爬的基本操作

京东的反爬机制会让频繁请求的 IP 直接失效,返回的页面里没有商品数据。系统采用了两层应对:设置代理 IP 池和每次请求后睡眠。代理 IP 放在 requests 的 proxies 参数里,建议维护一个 IP 列表,每次请求随机取一个。睡眠时间不是固定值,而是用 random.uniform(1, 3) 生成随机延时,避免被识别出固定节奏的机器人行为。

关于评论数据,京东对部分商品的评论详情页做了限制——只有前几页有完整评论内容。所以这里只抓取每件商品的前 10 页评论,超过的放弃。不要试图死磕把全部评论抓完,抓取成本远大于分析价值。还要注意设置 User-Agent,模拟浏览器的请求头,不要暴露 Python 默认的 urllib 标识。

3.3 字段采集与 CSV 落盘

抓下来的数据先写入 CSV 文件,不直接入库。原因是采集下来的数据格式差异很大:有的商品详情页没有上市年份,有的没有机身重量,字段缺失情况参差不齐。先落 CSV 再做清洗,比直接入库再改表结构灵活得多。写入 CSV 用 pandas 的 to_csv 方法,注意 encoding 参数设为 utf-8-sig,否则 Excel 打开会乱码。

4. 数据清洗与 MySQL 入库:Pandas 操作与入库规则

4.1 清洗规则:哪些数据必须删,哪些必须填

数据清洗是本系统的核心环节,直接决定后续可视化的准确性。项目里制定的清洗规则可以总结为四类:去重、去异常、填空值、统一格式。去重使用 drop_duplicates 函数,按商品 ID 去重;去异常是删除明显不合理的数据,比如价格小于 100 元的手机(大概率是配件或空壳链接)、机身重量小于等于 100g、机身长度小于 131mm,这些数据明显不符合手机的基本物理属性。

空值处理上有个值得借鉴的决定:不直接删除含空值的行,而是把空值填充为“其他”。原因在原文里写得很清楚——数据有限,删行会损失样本量。这个思路在真实项目里很实用:如果数据量不大,删行的代价可能比填充更大。品牌名的中英文不统一问题,用 replace 函数做映射替换。比如英文品牌名替换成中文常用名,这一步不做的话后续 group by 会把同一个品牌拆成两个统计项。

4.2 清洗代码实现与参数说明

import pandas as pd import pymysql # 连接 MySQL,charset 必须指定 utf8 否则中文乱码 con = 'mysql+pymysql://root:123@localhost:3306/jdsystem?charset=utf8' conn = pymysql.connect(host='localhost', user='root', password='123', db='jdsystem', charset='utf8') cur = conn.cursor() def clean_info(): # 读取爬虫落盘的 CSV 文件 df = pd.read_csv('../crawl/data/jdInfo.csv') # 去除重复值,subset 指定按商品 ID 判断重复 df = df.drop_duplicates(subset=['id']) # 删除不合理数据:价格小于100元、重量小于等于100g、 # 长度小于13.1cm的数据直接扔掉 df = df.drop(df[(df['price'] < 100) | (df['weight'] <= 100) | (df['length'] < 131)].index) # 空值填充为“其他”,不删行避免样本量损失 df = df.fillna('其他') # 品牌中英文统一替换,比如 'HUAWEI' -> '华为' df['brand'] = df['brand'].replace({'HUAWEI': '华为', 'Xiaomi': '小米'}) # 清洗后的数据写回 MySQL for row in df.itertuples(): sql = "INSERT INTO p_info (brand, shop, title, os, price) VALUES (%s, %s, %s, %s, %s)" cur.execute(sql, (row.brand, row.shop, row.title, row.os, row.price)) conn.commit()

drop 函数里传入的是一个布尔索引,这个写法比逐行判断高效得多。注意 drop 之后要重新赋值给 df,否则原数据不会变。fillna 的参数除了固定值,也可以用 method='ffill' 做前向填充,但这里的场景用固定值“其他”更合适。写入 MySQL 用参数化查询(%s 占位符),一定不要用字符串拼接,否则 SQL 注入风险和一个引号错位就能让入库脚本挂掉。

4.3 结巴分词:评论内容的前置处理

对用户评论内容做词云之前,必须经过结巴分词和词频统计。这里需要加载中文停用词表,把“的”“了”“是”这类无意义词过滤掉,否则词云里全是没有信息量的虚词。加载停用词表的常见做法:

import jieba stopwords = set() with open('../data/stopwords.txt', 'r', encoding='utf-8') as f: for line in f: stopwords.add(line.strip()) def tokenize(text): words = jieba.lcut(text) # 过滤停用词、单字词和纯数字 return [w for w in words if w not in stopwords and len(w) > 1 and not w.isdigit()]

jieba.lcut 返回的是分词后的列表。过滤条件里的 len(w) > 1 是为了去掉单字词,分词结果里大量单字词往往是噪声。停用词表需要自己维护,不同场景的词表差异很大——做手机评价分析,可以把“手机”也加进停用词,不然这个词的词频会碾压其他一切有意义的关键词。

5. 可视化实现与 Flask 数据对接:ECharts 图表配置实战

5.1 图表选型:条形图、饼图、柱状图、词云图的场景匹配

项目根据数据特点做了图表选型:统计店铺拥有手机数量用条形图,因为条形图适合展示排名型数据;统计手机上市年份用折线图,展示趋势变化;统计摄像头数量用饼图,展示占比结构;统计屏幕刷新率分布和运行内存用饼图;统计充电器功率、屏幕材质类型用柱状图。这个选型逻辑是通用的:排名看条形图、趋势看折线图、占比看饼图、对比看柱状图。

5.2 Flask 后端:把 MySQL 查询结果变成 JSON

Flask 后端的工作量不大,但有几个坑要提醒。先写一个查询函数从 MySQL 提取统计数据,再通过 Flask 路由返回 JSON。比如统计各品牌手机数量:

from flask import Flask, jsonify, request import pymysql app = Flask(__name__) @app.route('/api/brand_count', methods=['GET']) def brand_count(): conn = pymysql.connect(host='localhost', user='root', password='123', db='jdsystem', charset='utf8') cur = conn.cursor() cur.execute("SELECT brand, COUNT(*) FROM p_info GROUP BY brand ORDER BY COUNT(*) DESC") rows = cur.fetchall() # 拆成两个列表,方便 ECharts 的 xAxis 和 series 直接使用 brands = [r[0] for r in rows] counts = [r[1] for r in rows] return jsonify({'brand': brands, 'brandNum': counts})

这里把查询结果拆成两个平行列表返回,而不是返回字典列表,是为了前端 ECharts 的 option 配置更方便——xAxis 的 data 直接接收 brands,series 的 data 直接接收 counts。如果返回字典列表,前端还要再做一层 map 转换,纯属浪费。Flask 路由的 methods 参数明确指定 GET,避免不必要的 POST 请求误入。

5.3 ECharts 前端配置:条形图的 option 详解

品牌手机数量条形图的 ECharts 配置是整个项目里最标准的图表模板,可以直接复用:

var option1 = { title: { top: 7, left: 20, text: '品牌拥有手机数量排行' }, tooltip: { trigger: 'axis', axisPointer: { type: 'none' } }, legend: { left: 500, top: 10, data: ['数量'] }, toolbox: { show: true, feature: { saveAsImage: { show: true }, dataView: { show: true } } }, xAxis: { type: 'category', axisLine: { show: false }, data: json_data['brand'] }, yAxis: { type: 'value', axisLabel: { show: true } }, series: [{ name: '数量', type: 'bar', data: json_data['brandNum'], itemStyle: { barBorderRadius: 5 } }] }; myChart1.setOption(option1);

tooltip 的 trigger 设为 'axis' 意味着鼠标悬停时按坐标轴维度显示提示,比默认的 item 更适合条形图这种按分类展示的场景。legend 的 left 设 500 是因为大屏布局中多个图表共存,需要手动调整位置避免重叠;如果单图展示,left 用 'center' 即可。itemStyle 的 barBorderRadius 是 ECharts 5 里圆角柱子的配置,属 UI 细节但很影响观感。axisLine 设为 false 隐藏 x 轴的轴线,视觉上更干净。

5.4 词云图实现:ECharts 的词云需要额外插件

系统要展示用户评价标签和评论内容的词云图,但原生 ECharts 不带词云图组件,需要引入 echarts-wordcloud 插件。词云图的数据源是结巴分词后统计的词频,格式为 [{ name: '华为', value: 123 }, ...]。词云的渲染效果受两个参数影响:shape 决定词云的轮廓形状(circle、diamond 等),sizeRange 控制字号范围。字号大小与词频正相关,所以词频统计的准确性直接影响视觉效果——这也是停用词表要维护好的原因。

6. 避坑指南:爬虫到可视化全链路常见问题排查

6.1 爬虫请求返回空白页面

现象:requests 请求京东列表页,返回的 HTML 里没有任何商品数据,打印出来的内容长度不到 1KB。

原因:京东对高频请求做了 IP 封禁或验证码拦截,裸 requests 请求很容易被识别。也可能是 URL 参数顺序不对,京东对参数顺序敏感。

解决:先检查 User-Agent,必须在请求头里伪装浏览器。然后加随机睡眠时间,用 time.sleep(random.uniform(1, 3))。如果还不行,启用代理 IP。验证 URL 是否正确的办法是手动在浏览器打开同样的 URL,如果能正常显示页面,说明 URL 没问题,问题在请求头或 IP。

6.2 清洗后的数据写入 MySQL 报错

现象:pymysql 写入时报错 "Incorrect string value: '\xE5\x8D\x8E...' for column 'brand'",中文数据写不进去。

原因:表的字符集不是 utf8,或者连接字符串的 charset 参数没设置。京东采集的数据里有大量中文品牌名和商品名,字符集不匹配就会报这个错。

解决:建表时指定 DEFAULT CHARSET=utf8,连接字符串里加上 charset='utf8'。已经建错的表用 ALTER TABLE p_info CONVERT TO CHARACTER SET utf8mb4 修改。注意 utf8mb4 比 utf8 更全,能存下 emoji 表情,如果评论区有特殊符号建议直接用 utf8mb4。

6.3 ECharts 图表不显示,控制台报错

现象:页面加载后图表区域空白,浏览器控制台提示 "Cannot read property 'getContext' of null"。

原因:ECharts 初始化时传入的 DOM 容器还没渲染完成,或者容器的 id 对不上。最常见的是把初始化脚本放在 body 前面,此时容器元素还不存在。

解决:把初始化脚本放在页面底部( 之前),或者用 window.onload 包一层。检查容器 div 是否有明确的宽度和高度,ECharts 默认宽高为 0 时不会渲染任何内容。

6.4 Flask 返回 JSON 时出现 TypeError

现象:jsonify 报错 "Object of type 'int64' is not JSON serializable"。

原因:pandas 的 groupby 结果里数据类型是 numpy.int64,不是 Python 原生的 int,jsonify 无法序列化。

解决:在返回前做类型转换,用 int() 或 float() 包裹数值,或者用 df 的 tolist() 方法把整列转成 Python 列表。这个坑几乎每个用 pandas 接 Flask 的项目都会踩,可以在工具函数里统一做一次转换。

6.5 爬取过程中断后重新跑,数据重复

现象:第一次爬了 3000 条数据,程序中断后重跑,数据库里出现 6000 条,其中一半重复。

原因:没有去重机制。重跑爬虫会重新写入同样的数据,而 p_info 表的 id 字段没有设置唯一约束。

解决:两个层面处理。一是给 MySQL 表的 id 字段加 PRIMARY KEY 或 UNIQUE 约束,重复写入会直接报错;二是清洗脚本里的 drop_duplicates 指定 subset 参数,按业务唯一键(比如商品链接 URL)去重。建议两个层面都做,数据库约束兜底,pandas 清洗保证业务正确。

7. 进阶技巧:词云图参数调优与图表主题风格定制

词云图是这套系统里最出效果的图表,也是参数调整空间最大的地方。做用户评价词云时,shape 参数我用过 'circle' 和 'diamond',实际效果 circle 更适合商品评价场景,视觉焦点居中,高频词向中心聚拢;diamond 适合大屏装饰性展示。更重要的是 minRotation 和 maxRotation 这对参数,控制词云里文字的旋转角度范围,我通常设为 0 和 90,只保留横排和竖排两种方向,避免出现 30 度、45 度这种看着杂乱的角度。还有一个容易忽略的点:词云图的文字颜色建议用 ECharts 的 visualMap 组件根据词频做渐变映射,高频词用深色,低频词用浅色,层次感会出来。

图表主题风格可以统一处理。项目里多个图表共存,每个都单独写 option 会越来越难维护。我一般抽一个 commonTheme 对象,把 fontFamily、colorPalette、tooltip 的通用样式放进去,每个图表用 ECharts 的 merge 机制覆盖自己的差异项。大屏的配色建议控制在三到四种主色内,不要每个图表用不同色系,否则视觉上会乱。

我自己在跑这套系统时,最有挫败感的一步不是爬虫不是清洗,而是花了半小时查 ECharts 图表不渲染的原因——最后发现是容器 div 的 height 写成了百分比但父级没有高度。从那以后我每次写可视化前端,都强制先检查容器宽高、再检查数据格式,最后才看 option 配置,这个排查顺序成了固定习惯。做数据可视化系统,链路越长越要培养这种“先定位再修改”的思维,希望这套项目笔记能帮你在自己的数据可视化项目里少走几步弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 11:03:01

MicroLogix1400实战指南:RSLogix500与RSLinx Lite直连调试

1. 这不是“软件安装教程”&#xff0c;而是真实产线工程师的MicroLogix1400上手实录 MicroLogix1400——这个在2008年就停产、2017年正式终止技术支持的PLC型号&#xff0c;今天依然稳稳趴在华东某食品包装厂的灌装线上&#xff0c;控制着三台伺服电机同步推瓶、两组气动夹爪精…

作者头像 李华
网站建设 2026/10/5 11:01:40

RC电路设计失效真相:时间常数稳定性与高频寄生效应实战解析

1. 为什么一个看似简单的RC电路&#xff0c;总在实际设计中“不听话”你有没有遇到过这样的情况&#xff1a;照着教科书画了个RC低通滤波器&#xff0c;截止频率标称1kHz&#xff0c;结果实测下来——3dB点偏移到了700Hz&#xff0c;相位响应歪得不像样&#xff1b;或者用RC给运…

作者头像 李华
网站建设 2026/10/5 11:01:05

PostgreSQL 16 DML实战指南:INSERT、UPDATE、DELETE从入门到精通

从入门到擅长 DML&#xff0c;这对 PostgreSQL 16 像是老生常谈&#xff0c;但实际生产环境里&#xff0c;我见过太多次因为一条 UPDATE 漏了 WHERE、一条 DELETE 条件写反引发的线上事故。这系列教程写到第 8 篇&#xff0c;终于把增删改这三板斧掰开来讲。今天这篇不是简单念…

作者头像 李华
网站建设 2026/10/5 11:01:00

rnn基本介绍,词嵌入,循环网络侧层

小案例jieba分词&#xff08;词嵌入层&#xff09;"""案例:RNN全称叫: Recurrent neural network, 循环神经网络, 包含: 词嵌入层 循环层 全连接层(输出层)其中:词嵌入层: 把词转成词向量矩阵, 即: word_to_vector(word_2_vector, word_vec, word2vec)思路:语…

作者头像 李华
网站建设 2026/10/5 10:58:54

Polyworks脚本开发入门:环境搭建与第一个自动化宏

做三维测量的朋友应该都有过这种经历&#xff1a;来了一批新零件&#xff0c;要挨个加载CAD模型、对齐基准坐标系、跑一遍检测路径、再导出一份PDF报告&#xff0c;整套手动操作下来快则十分钟&#xff0c;慢则半小时&#xff1b;批量件一多&#xff0c;一天大半时间都耗在重复…

作者头像 李华
网站建设 2026/10/5 10:57:45

微服务架构下超市库存管理系统开发复盘:SpringBoot+Vue+SpringCloud实战

微服务分布式SpringBootVueSpringCloud的超市购物采购库存管理系统——这是一类很典型的“听起来高大上、落地全是坑”的项目&#xff0c;我之所以想专门写一篇总结&#xff0c;是因为这套系统几乎把微服务开发里最常见的几个硬骨头都踩了一遍&#xff1a;服务怎么拆分、远程调…

作者头像 李华