基于Selenium与Flask的电商数据采集分析与销量预测系统

发布时间:2026/10/3 14:21:41
基于Selenium与Flask的电商数据采集分析与销量预测系统 每年到了毕设启动的时间总有不少同学拿着类似的题目来问我Python爬虫、电商数据分析、销量预测。看得出来大家都想做一套有含金量、能拿得出手的系统但真正动手前又担心整套流程太长、技术点太杂怕做到一半做不下去。这套“python电商数据采集分析与销量预测系统”恰好就是一条非常完整的链路用Selenium做商品数据采集用机器学习建销量预测模型最后用Flask把分析结果和预测结果以可视化页面呈现出来。把它彻底拆开之后你会发现它既不是一个纯爬虫项目也不是一个纯算法项目而是一个把人工智能力量和工程能力结合得很好的毕业设计选题。这篇文章我就以完整项目经验的角度把从选型、采集、建模到展示涂装的每一步都讲清楚尤其是那些指导书不会写、只有真正跑过一遍才会踩到的坑。我见过太多人在“数据采集”这一步就卡住了requests请求回来的页面是空的商品信息全都在JavaScript里藏着也有人好不容易把数据抓下来结果发现清洗之后根本没法用还有人模型训练完R方看着很高一测试就崩。这些问题的根源往往不是某个技术不够熟练而是缺失了一条清晰的工程主线。这篇内容就是想把这条主线串起来让无论你是在做毕业设计、课程设计还是单纯想入门数据采集和机器学习结合的项目都能照着它把整套东西跑通。1. 毕设选题的门道为什么“电商数据采集销量预测”值得做1.1 先看清这套系统的完整面貌这个题目听起来好像很庞大其实拆开看就是四个模块数据采集层用Selenium模拟真实浏览器跑商品搜索、翻页、详情抓取拿到商品标题、价格、销量、评论数、店铺信息等原始数据。数据治理层把抓到的数据做清洗、去重、缺失值处理、字段规整形成结构化的数据分析底表。算法预测层基于历史销量数据、价格波动、评论指标等特征用机器学习模型训练销量预测模型输出未来一段时间销量预测。可视化展示层用Flask搭建Web应用将统计图表和预测结果展示出来形成可操作的演示界面。这种结构最讨巧的地方在于它同时覆盖了数据采集、数据处理、机器学习、Web开发四个方向任何一个环节都能在答辩时展开细讲不用怕回答问题没素材。市面上很多毕设题目要么是纯管理系统图书管理、学生管理要么是纯算法演练跑一个CNN识别猫狗图片前者太单薄后者太“玩具”。而这个题目是真实的电商业务里每天都在发生的事情——商家需要知道未来商品能卖多少平台需要监控商品价格和销量走势技术人员需要打通全链路数据管道。1.2 这套题为什么不会做到一半就翻车我对做过毕设的学生有一个判断标准一个题目能不能做下去取决于它的“最小可用版本”能不能在两天内跑通。这个题目恰好满足。第一天你只需要用Selenium抓一个小分类下的几十个商品存进数据库第二天就能用pandas读出来画一张简单的折线图。只要这个最小闭环跑通剩下的工作全是“往管道里加东西”心理压力会小得多。反过来看那些一上来就定“深度学习销量预测”的同学十有八九会卡在数据量不够导致模型不收敛、环境配不明白、LSTM训练时间太长等问题上。我的建议是先做机器学习版本把线性回归、随机森林、XGBoost都试一遍如果数据量大、时间序列特征明显再拿深度学习做对比实验。这样即使深度学习效果一般也属于“做了尝试并给出了合理解释”答辩是加分项而不是减分项。2. 系统框架搭起之前需要先想清楚的几件事2.1 数据往哪存SQLite还是MySQL这可能是一开始就要做的决定。我的经验是如果不具备一次性把数据量冲到几十万以上的条件先直接用SQLite。SQLite是Python自带的数据库零配置、单文件、迁移方便对毕设演示来说完全够用。你不需要装MySQL服务也不需要处理复杂的用户权限和端口占用问题大把时间可以省下来做算法和页面。但是答辩的时候很容易被问到“如果数据量大了怎么办”。这个问题其实是考察你有没有数据库横向扩展的意识。你完全可以说当前阶段用SQLite保证项目轻量但我们的数据表设计已经预留了迁移到MySQL的条件表结构遵循规范化设计后续只要把连接字符串换成pymysql即可。我在实际项目里就是这么处理的先用SQLite快速迭代等功能稳定后再切MySQL整个过程不超过半小时。表格设计上我会建议至少三张表表名核心字段作用goodsid, 商品标题, 店铺, 品牌, 商品链接, 分类, 首次采集时间商品主表存去重后的商品信息price_historyid, 商品id(外键), 价格, 采集时间记录时间维度上的价格变化画趋势图sales_recordid, 商品id(外键), 销量, 评论数, 好评率, 采集时间建模所需的销量和口碑指标用于训练预测模型这套表结构看起来简单但它是整个系统的地基。很多同学喜欢把价格和销量存在一堆字段里后面做时间序列分析时才发现数据没法对齐再去拆表就是灾难。2.2 Flask项目怎么组织才能不变成一坨混乱代码Flask项目虽然不大但如果把所有功能集中写在一个app.py里后面改动维护会让你很难受。我的经验是一开始就按功能模块分层project/ ├── app.py # Flask入口注册路由 ├── config.py # 数据库连接、配置参数 ├── models.py # SQLAlchemy表模型 ├── spider/ │ ├── collector.py # Selenium采集器 │ ├── parser.py # 页面解析、数据清洗 │ └── run_spider.py # 采集调度入口 ├── analysis/ │ ├── feature_engineer.py # 特征工程 │ ├── model_train.py # 模型训练与评估 │ └── forecast.py # 调用模型做预测 ├── static/ # 前端静态文件js/css ├── templates/ # Jinja2模板 └── requirements.txt这样做的好处是答辩时你可以很清楚地跟老师说采集逻辑和业务逻辑解耦算法模块可以独立替换。这在软件工程评分里是很加分的点。而且实际上当你换了数据源、改了采集网站或者想换模型时只需要动对应模块不会把整个项目搞崩。2.3 可视化选型不要纠结Pyecharts就是最快路径做数据可视化最稳妥的组合是Pyecharts Flask模板渲染。Pyecharts的底层其实调用的是ECharts图表交互效果好而且它可以直接在Python里把图表生成HTML片段嵌入Jinja2模板不需要你写好几百行JavaScript。价格走势图用折线图Line商品价格分布用散点图Scatter销量贡献度用饼图Pie评分分布用直方图Bar这些基础图表就能覆盖90%的展示需求。如果你不想用Pyecharts也可以直接用前端ECharts通过JSON接口动态加载数据。这两种方案我都试过对毕设来说Pyecharts上手快但如果你已经有了一点前端基础走JSON接口的方式会让整个页面更“活”图表可以和用户交互、联动筛选。我的建议是时间紧就Pyecharts想做出更精致的演示效果就学一点ECharts基础把后端算好的数据用fetch取回来再渲染。3. 基于Selenium的商品数据采集比requests多走的几步3.1 为什么非要用Seleniumrequests不行吗很多电商网站的商品列表页数据并不是在最初返回的HTML源码里的而是通过JavaScript异步请求动态渲染出来的。你用requests拿到的那一串HTML里可能只有页面框架商品名称和销量数据根本不存在。这时候Selenium的价值就体现出来了它模拟一个完整浏览器等JavaScript执行完再从渲染后的DOM里取数据。我做过对比测试同一类商品页面requests拿到的可解析字段数量大概只有Selenium的三分之一到二分之一而且经常会出现“标签存在但内容为空”的情况。所以在电商场景里Selenium算不上“重”它是在动态渲染环境下的必要选择。当然它也有代价慢、吃内存、容易被检测但这些代价在毕设体量下完全可接受。3.2 页面滚动、懒加载与翻页处理的三件套这是采集过程中最重要的细节。电商列表页为了追求加载速度普遍采用图片和数据的懒加载你滚动到哪里数据才加载到哪里。如果你不滚动就让程序直接去抓页面元素大概率只能拿到首页前几屏的少量商品后面全都是空白占位符。核心的滚动逻辑我一般这样写from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.action_chains import ActionChains import time def scroll_to_bottom(driver, max_times10): 模拟鼠标缓慢下滑触发懒加载 for _ in range(max_times): driver.execute_script(window.scrollBy(0, 1200);) time.sleep(0.8) scroll_height driver.execute_script(return document.body.scrollHeight) current_height driver.execute_script(return window.scrollY window.innerHeight) if current_height scroll_height: break这里有一个容易被忽略的点不要用“瞬间跳到页面底部”的滚动方式因为那种滚动不会触发懒加载——网站前端通常会检测滚动频繁程度来区分真实用户和脚本。用“每次滚动一小段延时”的方式更像真人行为数据加载的成功率高很多。翻页的逻辑也类似要先去检测“下一页”按钮是否存在再点击再等页面加载完。你可以在点击前后都打印当前页码方便排查连续翻页失效的问题。热搜词里能看到“selenium网页左右滑动”被反复搜索其实就是横向滚动目录或图片轮播的采集问题同样可以用ActionChains拖拽或scrollBy方式处理思路一致。3.3 WebDriver被识别为机器人的应对方法这是Selenium采集最核心的坎明明能打开浏览器但抓取到的页面提示“访问异常”或登录弹窗挡住所有数据甚至干脆封了IP。原因在于浏览器可以通过脚本检测到当前窗口是自动化工具控制的最常见的信号就是window.navigator.webdriver属性为true。针对这个问题一个常规处理思路是options webdriver.ChromeOptions() options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_argument(--disable-blink-featuresAutomationControlled) driver webdriver.Chrome(optionsoptions)这两行配置能做掉一部分检测。但要真正更接近真人还要做几件配套的事给浏览器设置一个常见的User-Agent不要用自动化工具的默认值每次请求之间加入随机延迟比如time.sleep(random.uniform(1.5, 3.5))如果是登录后才能看到的页面先在浏览器里手动扫码登录一次把登录Cookie保存到本地下次采集自动带上。Cookie保存和加载可以用seleniumwire或手动把driver.get_cookies()序列化到文件里再次启动时先访问一次网站首页再通过add_cookie把登录态写入浏览器。这套方案我在实际测试里能把采集成功率从不足六成提升到九成以上。3.4 数据入库与断点续采设计采集过程最怕跑到一半崩掉前面几个小时的成果白白浪费。我在设计这个毕设时加了一个非常简单的“断点续采”机制在goods表里加一个status字段已经完整采集过的商品标记为1未完成的标记为0每次重新启动采集脚本时先查一下哪些商品还缺数据只补采缺失的部分。这样即使中途网络挂了、浏览器卡了下次重跑也能从上次断掉的位置继续不需要清空重来。入库时还要注意一个细节商品链接去重。同一个商品在搜索结果里可能出现两次如果不去重直接插入后面做统计分析时会产生重复统计的脏数据。我通常的做法是先根据商品链接生成哈希值作为唯一标识插入前先查库已经存在就跳过只更新价格历史和销量记录。这一步能在源头解决很多数据质量问题。4. 销量预测的核心逻辑从数据清洗到模型选择4.1 机器学习里的“数据处理”到底是什么很多同学在上机器学习课程时最懵的就是老师反复强调“数据处理很重要”但到底处理什么、怎么处理没有一个直观的概念。放到这个电商场景里它就变得很具体了。你从数据库里读出来的原始数据一般是这个样子的商品标题 | 价格 | 销量 | 评论数 | 好评率 | 采集日期但这个数据不能直接喂给模型因为模型只认数值。像“商品标题”这种文本信息要么丢弃要么用某种方式转成数值特征日期字段要拆成年、月、日、星期几或者换算成距离当前的时间差“好评率”如果有些字段缺失还需要决定用均值填充还是中位数填充。这就是机器学习的标准数据处理步骤缺失值处理、异常值过滤、特征编码、归一化或标准化、特征构造。你可以把原始数据想象成一堆浑浊的泥沙水特征工程就是滤网和沉淀池把对预测有用的东西留下来把噪音排掉。巧妇难为无米之炊这个环节做得越扎实模型的天花板就越高。4.2 销量预测本质上是回归问题不是分类问题首先要明确一个概念销量预测是回归问题模型输出的是一个连续数值比如明天这件商品可能卖出35件而不是分类问题比如“卖得好/卖得不好”。认清这一点你就不会去用分类模型硬套也不会用分类准确率去评估结果。我常用的基线模型是线性回归它有两个好处一是训练速度快二是能给每个特征一个权重系数方便解释哪些因素最影响销量。比如拟合出来“价格”的系数是负的说明价格越高销量越低这个结论在答辩时非常好讲。但线性回归对非线性关系的拟合能力有限所以通常还要试试随机森林和XGBoost。随机森林能处理特征之间的非线性交互泛化能力稳定而且对参数不敏感适合作为主力模型XGBoost则是在数据量足够、追求更高精度时的更好选择只是需要多花时间调参并注意过拟合。4.3 特征工程预测精度的真正天花板如果让我只给一个建议那就是把80%的精力放到特征工程上而不是模型调参上。同样的算法特征构造得好坏结果差距可能非常明显。针对这个电商场景我建议构造这些特征销量历史特征过去7天平均销量、过去14天平均销量、销量环比增长率、上月同期销量。这些特征能捕捉销量的趋势性和周期性。价格相关特征当前价格、近30天价格波动幅度、价格是否比历史最低价低。促销降价往往是销量上升的重要驱动。商品口碑特征评论总数、好评率、近7天新增评论数。一个新商品如果评论数暴涨往往意味着销量也在快速上涨。时间特征星期几、是否节假日附近、距离大促节点的天数。电商销量有明显的周末效应和节日效应。竞品环境特征如果能采到同类商品平均价格、平均销量。这个特征能让模型知道商品在同类中的生态位。特征构造的代码用pandas实现非常直接import pandas as pd df pd.read_csv(sales_data.csv, parse_dates[collect_date]) df[weekday] df[collect_date].dt.weekday df[is_weekend] df[weekday].apply(lambda x: 1 if x 5 else 0) df[price_drop_ratio] (df[price_high_30d] - df[price]) / df[price_high_30d] df[sales_ma7] df.groupby(goods_id)[sales].transform(lambda x: x.rolling(7, min_periods1).mean())这里要特别提醒一个很容易犯的错误特征构造时不能使用未来数据。比如你要预测明天的销量那么“明天的价格”和“明天的评论数”都是此刻不存在的。很多人在做特征时因为数据已经全部采完了就顺手把前后几天的数据都用了导致模型测试时效果惊艳一上线预测就废。正确做法是构造特征时只使用预测时刻之前的数据这就是机器学习里常说的“数据泄漏”问题。4.4 模型训练、评估与“看着高分却没用”的陷阱训练流程我用的是sklearn的标准流程from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, r2_score X feature_df.drop(columns[sales_target]) y feature_df[sales_target] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, shuffleFalse) model RandomForestRegressor(n_estimators200, max_depth8) model.fit(X_train, y_train) y_pred model.predict(X_test) print(R2: , r2_score(y_test, y_pred)) print(MAE: , mean_absolute_error(y_test, y_pred))这里面有一个细节shuffleFalse。因为销量数据是时间序列如果随机打乱训练集和测试集模型相当于“偷看”了未来数据测试结果会虚高。用顺序切分模拟真实的预测场景训练集在前、测试集在后这样评估出来的MAE和R2才有参考价值。还有个常见的坑样本量太少时R2可能看着很高其实没有泛化能力。我见过有同学采了一百多条数据随机森林训练集R2跑出0.98测试集也有0.9左右感觉完美但一换到新商品就完全失效。这种情况多半是特征太稀疏、数据量太少。解决思路有两种一是扩大采集范围把几十个商品的时间序列横向堆叠用“商品时间”做样本扩大样本量二是缩小预测目标不做“具体销量件数”预测改成“销量趋势档位预测”也就是把它变成分类问题牺牲精度但稳定性更好适合演示场景。具体选哪种要看你的数据和答辩想强调的方向。5. Flask可视化后台把预测结果变成真正能展示的系统5.1 路由设计与数据接口的最简打法Flask把页面和接口串联起来很直接我通常会先设计好路由清单再逐一实现/ - 系统总览页核心指标卡片 今日销量统计 /goods - 商品列表页支持按分类、按价格区间筛选 /analysis - 数据分析页价格走势图、销量分布、评分散布 /forecast - 销量预测页展示各商品未来N天的预测结果 /api/goods_data - JSON接口返回商品统计数据前端通过fetch动态更新图表写一个简单的数据接口并不复杂from flask import Flask, jsonify import pandas as pd app Flask(__name__) app.route(/api/goods_data) def goods_data(): df pd.read_sql(select * from goods, db_conn) result { total_count: len(df), avg_price: float(df[price].mean()), total_sales: int(df[sales].sum()), top_products: df.head(10).to_dict(orientrecords) } return jsonify(result)这样前端页面只需要fetch这个接口就能动态更新指标卡片和图表不用刷新整个页面。如果你不会写JavaScript也没关系直接在后端用Pyecharts生成HTML片段通过render_template嵌入页面即可。两种方式可以共存静态展示用Pyecharts交互联动用JSON接口。5.2 预测结果怎么展示才够“专业”光给一个预测数字是不够的答辩时老师最常问的是“你这个预测凭什么可信”。所以展示预测结果时我建议把三个东西一起呈现历史真实销量曲线、模型预测值、置信区间。置信区间可以用sklearn里的RandomForestRegressor各棵树的预测结果算标准差来近似或者简化为预测值加减一个经验误差幅度。图表上一旦出现3条线整个页面的专业感立刻就不一样了。# 用模型自带的多棵树估算不确定性 pred_all model.estimators_[::10] # 抽部分树减少计算量 pred_array np.array([tree.predict(X_predict) for tree in pred_all]) pred_mean pred_array.mean(axis0) pred_std pred_array.std(axis0) upper pred_mean 1.5 * pred_std lower pred_mean - 1.5 * pred_std这个做法本质上是把随机森林的“多个弱学习器投票”机制利用起来不需要额外引入复杂的概率模型就能给出一套有解释力的波动范围。我当时做的时候老师看到图上带有上下界带第一反应就是“这个项目考虑得比较完整”印象分一下就上去了。5.3 让系统每天自动跑起来毕业设计演示最怕的就是“当场没数据”或“数据是几个月前的”。真正演示的时候如果系统能自动采集新数据、自动更新预测效果会远超那些只能展示静态截图的项目。我用了APScheduler这个轻量级定时调度库在Flask启动时注册两个任务from apscheduler.schedulers.background import BackgroundScheduler import atexit def start_scheduler(): scheduler BackgroundScheduler() scheduler.add_job(funcrun_spider, triggercron, hour2, minute30) scheduler.add_job(funcretrain_model, triggercron, hour3, minute30) scheduler.start() atexit.register(lambda: scheduler.shutdown())解释一下思路凌晨2点半采集一次数据比真人购物高峰访问量小更不容易被限制3点半用新数据重新训练模型并更新预测结果。这样第二天你打开系统看页面上的数据就是今天最新的预测也是今天刚跑完的。整个流程自动化之后你答辩前的准备成本会低很多只需要保证电脑联网和浏览器驱动可用。6. 踩过的真实坑与答辩加分技巧6.1 爬虫采集环境里的典型坑先说我遇到的最无语的一个问题Chrome浏览器自动更新后selenium报“session not created: This version of ChromeDriver only supports Chrome version xxx”。这个问题的根源是Selenium的WebDriver和浏览器版本必须匹配Chrome一旦自动升级旧驱动就失效了。解决办法是使用与当前浏览器版本一致的ChromeDriver或者用能够自动匹配版本的webdriver-manager库from webdriver_manager.chrome import ChromeDriverManager driver webdriver.Chrome(ChromeDriverManager().install(), optionsoptions)这个库会自动检测当前浏览器版本并下载对应驱动省掉手工找驱动版本的过程。强烈建议在requirements.txt里加上它你的室友和学弟学妹复制你的环境时会真心感谢你。另一个容易踩的坑是XPath写得太脆。电商网站前端经常调整页面结构你昨天写的XPath今天可能就取不到数据了。我的应对经验是不要把XPath写满整条绝对路径尽量用相对定位和属性组合。比如用//div[contains(class,product)]//span[contains(class,price)]优先级比/html/body/div[3]/div[2]/div[1]/span[1]高得多。为了进一步降低风险写解析函数时可以做一层容错取不到数据时返回None而不要直接抛异常最后检查数据表里的空值比例就知道哪些字段的质量需要修复。6.2 机器学习部分最容易翻车的三个问题第一是日期格式解析失败。从网页采到的日期有时候是“2025-03-20 14:30:00”有时候是“2025年3月20日”还有可能是“3月20日”这种不完整的。用pd.to_datetime批量转换时经常报错或变成NaT。我的建议是采集阶段就把日期统一成标准格式字符串宁可多写一条正则规则也不要把脏日期留给下游。第二是数据量太小导致模型毫无意义。如果你只采了一两百条数据随机森林和XGBoost表现可能还不如用一个简单的“前7天平均销量”当预测结果。这时候不要硬上大模型而是可以把基线和机器学习做一个对比用数据告诉大家在数据量有限时简单规则也有价值当数据积累到一定程度模型优势才会显现。这种回答方式在答辩时显得非常务实比空谈算法先进要讨喜得多。第三是过拟合被老师问到哑口无言。建议你在展示模型效果之前先准备好一个“防追问方案”用交叉验证打印所有fold的评估分数如果发现训练集R2远高于测试集就说明过拟合。再把降低过拟合的操作限制树深度、增加min_samples_leaf、增加数据量列成一个清单老师怎么问都能接上话。6.3 Flask部署和演示现场的细节现场演示时最怕浏览器里页面打不开、接口超时、图表空白。我自己的经验是任何可能出问题的环节都要准备一个“本地兜底”。比如把曾经的采集结果导出一份CSV存到项目里一旦Selenium因为网络或站点反爬失效就从CSV读取数据继续展示保证系统“看起来还在工作”。不要把演示效果赌在学校的WiFi和目标网站的稳定性上。另外Flask默认的开发服务器是单线程的如果你的页面接口有很多个图表同时请求可能会卡。保险起见演示前把app.run(debugFalse)跑起来并关掉debug模式避免别的同学访问时触发调试器报错页面。如果你还想让网站在局域网内能被手机访问那就加上host0.0.0.0答辩现场用手机扫码打开也是一个很有记忆点的演示动作。6.4 答辩时的加分项设计最后说几个不费太多工夫但很加分的细节画一张系统架构图。不用很复杂把采集层、数据层、算法层、展示层用方框串起来标注每个环节用到的技术栈。答辩开场先投这张图老师瞬间就明白你做的是什么。准备一个特征重要性图。随机森林可以输出特征重要性这个图能直观告诉你“最影响销量的因素是价格还是评论数”是模型可信度的有力证据。说清楚系统可以怎么扩展。比如接入更多电商平台、增加用户画像模块、用更细粒度的时间序列模型做促销期间预测等。扩展思路不要求实现但能让老师觉得你有完整的工程思维。写一份简短的README把环境安装、启动命令、演示步骤写清楚。很多老师会当场翻项目文件一个干净清晰的README在软性评分上很有帮助。如果让我重新做一次这个题目我会把第一周的时间全部花在“最小闭环”上先抓50条数据、训练一个线性回归、用Flask出一张预测图。只要这个闭环转起来后面所有工作都只是堆料。很多同学做不完毕设不是能力不够而是第一步就冲着完美方案去了结果连一个能跑的demo都没留下。先把完整的链路走通再去打磨细节这套思路不仅适用于这个项目你以后做任何数据类项目都能沿用。