1. 项目概述
这个基于Python的京东手机数据分析与推荐系统,是我在指导学生完成毕业设计时开发的一个实战项目。作为一名长期从事数据分析和Python教学的技术从业者,我发现在电商数据分析领域,很多教学案例要么过于简单,要么脱离实际业务场景。因此,我设计了这个覆盖数据采集、清洗、分析和可视化全流程的项目,它不仅能帮助学生掌握Python核心技术栈,更能培养真实的商业数据分析思维。
系统采用Flask作为后端框架,通过requests库采集京东手机数据,使用pyecharts进行可视化展示,并实现了基于规则的推荐功能。整个项目代码量约2000行,涉及爬虫、数据库、Web开发和数据分析等多个技术领域。下面我将从技术选型、实现细节到避坑经验,全面剖析这个项目的开发过程。
提示:本项目所有代码和数据均已开源,文末会提供获取方式。建议读者边阅读边动手实践,遇到问题可以参考我的解决方案。
2. 技术架构设计
2.1 整体技术栈选型
选择合适的技术栈是项目成功的关键。经过多轮评估,我最终确定了以下技术组合:
- 后端框架:Flask(轻量级,适合快速开发)
- 数据采集:requests + 自定义Headers(避免被反爬)
- 数据存储:SQLite(单文件数据库,便于部署)
- 可视化:pyecharts(交互性强,图表类型丰富)
- 前端:HTML + Bootstrap(响应式布局)
为什么没有选择Django或Scrapy?在教学场景下,Flask的学习曲线更平缓,能让学生更专注于业务逻辑而非框架本身。而requests相比Scrapy更易于理解和调试,适合中小规模数据采集。
2.2 系统架构设计
系统采用典型的三层架构:
[数据层] ├─ 爬虫模块(requests) ├─ 数据库(SQLite) [业务层] ├─ 数据分析(Pandas/Numpy) ├─ 推荐算法(基于规则) [展示层] ├─ Web界面(Flask) ├─ 可视化图表(pyecharts)这种分层设计使得各模块职责清晰,便于后期维护和功能扩展。例如当需要增加新的数据源时,只需修改数据层的爬虫模块,不会影响其他部分。
3. 核心模块实现
3.1 数据采集模块
京东的数据采集有以下几个技术难点需要特别注意:
- 反爬机制:京东对爬虫检测严格,需要模拟浏览器行为
- 动态加载:商品数据通过AJAX异步加载
- 数据清洗:原始数据包含大量噪音
这是经过实战检验的爬虫代码片段:
def fetch_jd_phone_data(keyword, page): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Referer': 'https://search.jd.com/' } params = { 'keyword': keyword, 'page': page, 's': (page-1)*30 + 1 } try: response = requests.get( 'https://search.jd.com/Search', headers=headers, params=params, timeout=10 ) response.raise_for_status() # 解析HTML获取商品列表 soup = BeautifulSoup(response.text, 'html.parser') items = soup.select('.gl-item') data = [] for item in items: # 提取价格、标题、评论数等信息 price = item.select('.p-price strong i')[0].text title = item.select('.p-name em')[0].text.strip() comment = item.select('.p-commit strong a')[0].text data.append({ 'price': float(price), 'title': title, 'comment': process_comment(comment) # 评论数清洗 }) return data except Exception as e: print(f"爬取失败: {str(e)}") return []重要提示:实际项目中需要添加随机延迟(time.sleep)和代理IP池,避免触发反爬。我建议控制在3-5秒/请求的频率,并使用try-catch处理异常。
3.2 数据清洗与存储
原始数据需要经过以下处理步骤:
- 评论数转换:将"2万+"转换为20000
- 价格过滤:剔除异常价格(如999999)
- 品牌提取:从标题中提取手机品牌
清洗后的数据存入SQLite数据库,表结构设计如下:
CREATE TABLE phone ( id INTEGER PRIMARY KEY, brand TEXT NOT NULL, -- 品牌 image_url TEXT, -- 图片URL price REAL, -- 价格 name TEXT, -- 产品名称 detail_url TEXT, -- 详情页链接 comment TEXT -- 评论数据(JSON格式) );使用SQLite而非MySQL的原因:对于毕业设计级别的项目,SQLite完全够用,且部署简单(单个.db文件)。实际生产环境可以考虑迁移到MySQL或MongoDB。
4. 数据分析与可视化
4.1 销售分布分析
通过以下SQL查询获取各品牌销售数据:
def get_brand_sales(): conn = sqlite3.connect('jd_phone_info.db') cursor = conn.cursor() # 查询各品牌评论数总和(代理销量) sql = ''' SELECT brand, SUM( CASE WHEN json_extract(comment, '$.CommentCountStr') LIKE '%万%' THEN CAST(REPLACE(json_extract(comment, '$.CommentCountStr'), '万', '') AS REAL) * 10000 ELSE CAST(json_extract(comment, '$.CommentCountStr') AS INTEGER) END ) as total_comments FROM phone GROUP BY brand ORDER BY total_comments DESC ''' cursor.execute(sql) results = cursor.fetchall() return results使用pyecharts生成柱状图的关键代码:
from pyecharts.charts import Bar def draw_sales_bar(brand_sales): brands = [item[0] for item in brand_sales] sales = [item[1] for item in brand_sales] bar = Bar() bar.add_xaxis(brands) bar.add_yaxis("销量", sales) bar.set_global_opts( title_opts=opts.TitleOpts(title="各品牌手机销量分布"), xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45)) ) return bar4.2 价格分析
价格分析采用箱线图展示分布情况,核心是计算各品牌的价格统计量:
def get_price_stats(): conn = sqlite3.connect('jd_phone_info.db') df = pd.read_sql('SELECT brand, price FROM phone', conn) stats = df.groupby('brand')['price'].agg([ ('count', 'size'), ('min', 'min'), ('q1', lambda x: x.quantile(0.25)), ('median', 'median'), ('q3', lambda x: x.quantile(0.75)), ('max', 'max') ]).reset_index() return stats.to_dict('records')5. 智能推荐系统实现
5.1 推荐算法设计
考虑到毕业设计的复杂度和实用性,我们采用基于规则的推荐策略,主要考虑以下维度:
- 品牌偏好:用户指定感兴趣的品牌
- 价格区间:设置最低和最高价格
- 好评率:过滤低于阈值的产品
推荐接口的核心逻辑:
@app.route('/recommend') def recommend(): brand = request.args.get('brand') min_price = float(request.args.get('min_price', 0)) max_price = float(request.args.get('max_price', 99999)) min_rating = float(request.args.get('min_rating', 90)) conn = sqlite3.connect('jd_phone_info.db') cursor = conn.cursor() sql = f''' SELECT * FROM phone WHERE brand = ? AND price BETWEEN ? AND ? AND json_extract(comment, '$.GoodRate') >= ? ORDER BY json_extract(comment, '$.GoodRate') DESC LIMIT 20 ''' cursor.execute(sql, (brand, min_price, max_price, min_rating)) results = cursor.fetchall() return jsonify([dict(row) for row in results])5.2 推荐效果优化
在实际测试中发现几个问题:
冷启动问题:新用户没有历史数据
- 解决方案:提供热门品牌和价格区间的默认值
长尾效应:小众品牌推荐结果少
- 解决方案:加入相似品牌扩展(如选择"小米"时也推荐"红米")
评分偏差:不同品牌好评率基准不同
- 解决方案:使用Z-Score标准化处理
优化后的推荐逻辑流程图:
用户输入 → 参数校验 → 数据库查询 → 结果过滤 → 标准化处理 → 排序 → 返回TOP206. 部署与性能优化
6.1 项目部署方案
对于毕业设计演示,推荐两种部署方式:
本地运行:
pip install -r requirements.txt python app.py云服务器部署(以Ubuntu为例):
# 安装依赖 sudo apt update sudo apt install python3-pip pip3 install -r requirements.txt # 使用Gunicorn运行 pip3 install gunicorn gunicorn -w 4 -b 0.0.0.0:5000 app:app
6.2 性能优化技巧
在项目开发过程中,我总结了以下性能优化经验:
数据库索引优化:
CREATE INDEX idx_phone_brand ON phone(brand); CREATE INDEX idx_phone_price ON phone(price);缓存机制:
from flask_caching import Cache cache = Cache(config={'CACHE_TYPE': 'SimpleCache'}) cache.init_app(app) @app.route('/sales') @cache.cached(timeout=3600) # 缓存1小时 def get_sales_data(): # 耗时查询 return jsonify(data)异步加载: 前端使用AJAX异步加载图表数据,避免页面卡顿。
7. 常见问题与解决方案
7.1 爬虫被封禁
现象:请求返回403状态码或验证码页面
解决方案:
- 轮换User-Agent和代理IP
- 添加随机请求延迟
- 使用selenium模拟人工操作
7.2 数据可视化渲染慢
现象:页面加载时间过长
优化方案:
- 减少一次性渲染的数据量(如只显示TOP20品牌)
- 使用WebWorker进行后台计算
- 启用图表动画效果降级
7.3 推荐结果不准确
改进方法:
- 引入协同过滤算法
- 增加用户行为数据收集
- 使用机器学习模型预测偏好
8. 项目扩展方向
这个基础框架可以进一步扩展:
- 增加数据源:整合天猫、苏宁等平台数据
- 增强推荐:实现混合推荐(内容+协同过滤)
- 实时分析:使用Kafka+Spark Streaming处理实时数据
- 移动端适配:开发微信小程序版本
我在实际教学中发现,学生最常遇到的困难是反爬虫策略和数据分析方法的选择。建议先从小规模数据开始,逐步完善系统功能。对于想深入学习的同学,可以考虑引入以下技术:
- 使用Scrapy-Redis构建分布式爬虫
- 采用Django REST Framework重构API
- 集成TensorFlow实现销量预测
这个项目完整代码和数据集已分享在我的技术博客(示例URL),包含详细的部署文档和视频教程。在实际开发过程中,记得定期备份数据,并使用版本控制(如Git)管理代码变更。