尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于Streamlit与Spark的汽车销售分析系统:数据全链路实战

基于Streamlit与Spark的汽车销售分析系统:数据全链路实战 如果最近在找毕业设计选题你大概率见过类似的名字基于 Streamlit 的国内汽车销售分析系统。很多同学的第一反应是这又是一道“套壳网站”题用 Python 画几个图表再挂个爬虫抓点数据好像就能交差。但我看完这个选题反而觉得它比想象中更值得做——因为它真正考的不是你会不会用某个框架而是你有没有能力把一条完整的数据链路跑通从爬虫采集到存储再到计算分析最后用可视化把结果讲清楚。更关键的是这条链路上每一环都不是孤立的。Streamlit 只是最外面一层壳真正的复杂度在数据来源、数据质量、存储选型和处理引擎上。如果你只是把组件堆在一起确实一周能搭出 Demo但经不起深问。这篇文章我就以这个选题为线索把“数据采集—存储—计算—可视化”的完整设计过程拆开讲清楚每一步背后的为什么以及哪些地方最容易翻车。1. 先拆题这个毕设真正考的不是页面而是数据链路1.1 从标题拆出五层内容这个题目看起来是“Streamlit 汽车销售”但拆开之后至少有五层内容数据采集Python 爬虫负责拿到国内汽车销量相关的原始数据。数据存储数据规模上来之后不能永远用本地 CSV需要引入分布式文件系统比如 HDFS。数据处理Hadoop 和 Spark 在这里承担不同角色Hadoop 负责存储和资源管理Spark 负责计算和预处理。数据分析对销量做品牌对比、月度趋势、车型排行、市场份额变化等维度分析。可视化交互Streamlit 把分析结果变成可筛选、可查看的网页应用。如果只看标题很多人会以为难点在 Streamlit。但实际做下来Streamlit 反而是耗时最少的部分真正的精力都花在数据获取、清洗、格式转换和链路打通上。换句话说这个选题的重点不是“展示”而是“处理”。1.2 技术栈不是越多越好而是要找一条不走回头路的链路很多同学拿到题目后第一版方案是爬虫抓完数据存 CSV然后用 Pandas 清洗最后 Streamlit 读 CSV 展示。这个方案能跑通但它没有真正体现 Hadoop 和 Spark 的价值答辩时很容易被问住你的数据量到底多大为什么不用 Pandas 直接处理反过来如果一开始就把数据往 HDFS 上放用 Spark 做聚合再导出成 Streamlit 能读的格式链路上每一层都有明确的职责问起来也容易解释清楚。所以我更建议从一开始就按这条路径设计爬虫采集 → 原始 CSV → 上传 HDFS → Spark 清洗聚合 → 导出 Parquet → Streamlit 读取可视化这个流程看起来比“直接 Pandas 处理”多绕了几步但每一步都有教学意义。毕业设计要的不只是结果而是把课堂上学的大数据概念真正串起来。这条链路一旦跑通你会发现 Hadoop 和 Spark 的很多知识点会自动变得具象。2. Streamlit 的正确用法交互可视化是一层壳核心在数据组织2.1 为什么选 Streamlit而不是 Flask 或 Django先聊一个常见的疑问数据可视化页面为什么不用 Flask 或 DjangoFlask 和 Django 是通用 Web 框架它们更适合做有用户系统、有复杂业务逻辑、有表单提交和权限控制的 Web 应用。如果你只是要把分析图表展示出来用 Flask 意味着你要自己处理 HTML 模板、JavaScript 图表库、前后端接口传递这些工作对毕设来说属于重复造轮子。Streamlit 的思路完全不同。它允许你直接用 Python 写页面逻辑组件渲染、页面刷新、事件绑定都由框架处理。你写的是一个从上往下的 Python 脚本Streamlit 会把它转换成可交互的网页。这对数据分析类项目非常友好图表、表格、下拉框、滑块都能用几行代码完成。所以判断标准很简单如果你的项目核心是数据分析展示而不是复杂的 Web 业务Streamlit 会比 Flask 更合适。这也是这个题目选用 Streamlit 的底层原因——它把前端开发的成本降到最低让开发者把精力留给数据和算法。2.2 最小可用页面先让骨架转起来不要一上来就搭建复杂界面。先做一个最小页面确认 Streamlit 能读到数据再逐步加功能。一个常见的骨架长这样# app/main.py import streamlit as st import pandas as pd st.set_page_config(page_title国内汽车销售分析, layoutwide) st.cache_data def load_data(path): return pd.read_parquet(path) df load_data(data/processed/car_sales.parquet) st.title(国内汽车销售分析系统) st.write(f数据集包含 {len(df)} 条记录) brand st.sidebar.selectbox(选择品牌, [全部] df[brand].unique().tolist()) if brand 全部: filtered_df df else: filtered_df df[df[brand] brand] st.dataframe(filtered_df.head(200))这个页面虽然简单但已经包含几个重要的设计点st.set_page_config设置页面的基础显示配置。st.cache_data是 Streamlit 的一个关键装饰器。它会把加载结果缓存起来避免每次页面交互都重新读取文件。如果你的数据文件有几百 MB没有缓存的情况下每点一次筛选器都会卡住。侧边栏用selectbox做筛选这是数据分析页面最基础也最实用的交互方式。st.dataframe适合展示表格数据但如果数据量非常大建议先聚合再展示否则页面渲染会很慢。2.3 筛选、缓存与多页面让分析页面真正可用当数据可以正常读取后建议把不同分析主题拆成多个页面。Streamlit 支持“多页面应用”你只需要在app/pages/目录下创建 Python 文件每个文件就是一个独立的页面。例如可以这样组织app/ ├── main.py # 首页数据总览 └── pages/ ├── 1_品牌销量分析.py ├── 2_车型销量排行.py └── 3_市场趋势分析.py每个页面集中回答一类问题品牌销量分析各品牌月度销量变化、市场份额占比。车型销量排行Top 10 车型、价格区间与销量的关系。市场趋势分析整体销量随时间的变化、淡旺季规律。此时要注意不要在每一个页面都重新读取一遍全部数据。可以把数据加载函数放到一个公共模块里比如app/utils.py配合st.cache_data统一缓存避免多个页面重复消耗内存。这里最容易踩坑的地方是把原始数据直接塞进 Streamlit。最后页面会变得非常卡。更稳妥的做法是在 Spark 阶段把聚合结果计算好Streamlit 只负责读取和展示聚合结果。3. 数据从哪来爬虫采集、清洗与合规边界3.1 汽车销量数据可以来自哪里汽车销售数据分析系统的数据来源通常有两类公开的汽车销量统计报告或资讯页面。公开的数据集例如 GitHub 上有人整理的汽车销量数据 CSV。如果选择爬虫采集要注意数据来源的合法性。常见做法是采集公开的汽车资讯网站、销量统计网站的公开文章或页面而不是绕过登录、验证码或付费墙去抓取数据。国内汽车销量数据常见字段包括月份或年份例如 2024-01。品牌例如比亚迪、一汽大众、上汽大众。车型例如秦PLUS、朗逸、轩逸。销量当月销售数量。如果原始字段不足分析维度就会受限。所以爬虫阶段先想清楚你要分析哪些维度需要哪些字段支撑不要等到后期才发现少了关键字段。3.2 一个通用爬虫流程一个常见的爬虫采集流程是用requests获取目标页面 HTML。用BeautifulSoup或正则表达式解析表格或列表。把解析结果转换成 Pandas DataFrame。保存为 CSV 原始文件。下面是一个通用示例实际使用时要根据自己的数据来源调整选择器和字段import requests import pandas as pd from bs4 import BeautifulSoup headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } # 这里只做示例换成你自己确认合法且可访问的页面 url https://example.com/car-sales resp requests.get(url, headersheaders, timeout10) soup BeautifulSoup(resp.text, html.parser) rows soup.select(table tr)[1:] # 跳过表头 data [] for row in rows: cols row.find_all(td) if len(cols) 4: data.append({ month: cols[0].text.strip(), brand: cols[1].text.strip(), model: cols[2].text.strip(), sales: cols[3].text.strip().replace(,, ) }) df pd.DataFrame(data) df[sales] pd.to_numeric(df[sales], errorscoerce) df.to_csv(data/raw/raw_sales.csv, indexFalse, encodingutf-8-sig)采集时要注意访问频率。如果同时抓取大量页面建议在两次请求之间加time.sleep()并且合理设置请求头。这不是为了对抗什么而是避免对目标站点造成压力也是基本的网络礼仪。3.3 清洗规则字段名、缺失值和品牌口径爬虫拿到的是噪声很大的原始数据清洗阶段需要处理的问题很常见字段名不统一有的页面叫“厂商”有的叫“品牌”需要统一映射。销量字段混入逗号或单位例如“15,230 辆”需要去掉逗号和单位。品牌名称口径不统一例如“上汽大众”和“上海大众”可能指同一个品牌的不同写法需要建立品牌映射表。月份格式不统一例如“2024年1月”“2024-01”“202401”需要统一成yyyy-MM。缺失值处理如果某个月份的某车型没有销量数据是用 0 填充还是直接删除该行需要结合分析目标决定。清洗结果建议存成一份标准格式例如month | brand | model | sales 2024-01 | 比亚迪 | 秦PLUS | 23543 2024-01 | 一汽大众 | 速腾 | 13221这份标准数据可以继续上传到 HDFS交给 Spark 做进一步的聚合分析。4. Hadoop 和 Spark 在这个系统里到底承担什么角色4.1 先明确边界毕设规模不一定要上真实集群很多同学一听 Hadoop 和 Spark第一反应是“我要不要搭一个三节点集群”。答案是不需要。毕业设计的数据量通常在几万到几十万条级别用一台电脑跑 Spark 的 local 模式就够了。构建多节点集群会增加大量部署成本但对分析结果并没有实质性帮助。更合理的方式是用 Hadoop 的 HDFS 做文件存储体现分布式存储的应用。用 Spark 的 local 模式做数据处理体现分布式计算框架的编程模型。这就引出一个关键点引入 Hadoop 和 Spark不只是为了“跑得快”而是为了展示大数据处理的技术链路。哪怕数据量不大你也能在答辩时讲清楚如果数据增长到千万条甚至亿条这个架构如何扩展。4.2 推荐数据流CSV → HDFS → Spark → Parquet → Streamlit推荐的处理流程是爬虫生成的 CSV 文件先保存在本地data/raw/目录。将 CSV 上传到 HDFS 的某个目录例如/data/car_sales/raw/。Spark 从 HDFS 读取原始 CSV进行清洗和聚合计算。将处理完成的结果导出为 Parquet 格式可以仍然写回 HDFS也可以输出到本地。Streamlit 最终读取 Parquet 文件进行可视化。这个流程的好处是每一层的数据都是可追溯的。原始数据在 HDFS 上中间结果在 Spark 中展示数据在 Parquet 文件里不会出现“跑完就丢了”的情况。4.3 Spark 代码示例从月度销量到品牌聚合下面是一个简单的 Spark 处理示例。假设原始 CSV 已上传到 HDFS字段包括month、brand、model、salesfrom pyspark.sql import SparkSession from pyspark.sql.functions import col, to_date, year, sum spark SparkSession.builder \ .appName(CarSalesAnalysis) \ .master(local[*]) \ .getOrCreate() # 读取 HDFS 上的原始数据 df spark.read.csv( hdfs://localhost:9000/data/car_sales/raw/raw_sales.csv, headerTrue, inferSchemaTrue ) # 基础清洗统一日期格式过滤空销量 df df.withColumn(month_dt, to_date(col(month), yyyy-MM)) df df.filter(col(sales).isNotNull()) # 聚合按品牌和月份统计总销量 brand_month_sales df.groupBy(brand, month_dt).agg( sum(sales).alias(total_sales) ) brand_month_sales.orderBy(total_sales, ascendingFalse).show(20) # 导出为 Parquet brand_month_sales.write.mode(overwrite).parquet( hdfs://localhost:9000/data/car_sales/processed/brand_month_sales ) spark.stop()这个例子看起来简单但已经包含几个重要的 Spark 概念SparkSession 是统一入口。local[*]表示使用本地所有可用线程运行。DataFrame是 Spark 的分布式数据集抽象。groupBy和agg对应 MapReduce 思想中的聚合阶段。最终结果以 Parquet 格式写回 HDFS这是一种列式存储格式后续读取效率更高。如果想让系统更有深度还可以继续做计算每个品牌的市场份额。计算月度环比或同比变化。用窗口函数计算车型销量的滚动排名。把品牌销量与车型价格区间结合分析不同价位段的销量占比。4.4 什么时候可以不引入 Hadoop 和 Spark有一点要诚实如果数据量只有几千条Hadoop 和 Spark 的引入更多是教学意义而不是工程必要性。这个时候如果强行加入会被认为是为了“堆技术”而堆技术。但回到这个选题本身题目里明确写了 Hadoop、Spark、大数据分析所以它们是核心要求。合理的做法不是质疑它们有没有必要而是把它们放到合适的位置HDFS 存储原始数据Spark 做计算和预处理Streamlit 做结果展示。如果你以后想要更有说服力可以在论文或答辩中加一组对比数据同样的聚合任务用 Pandas 处理和用 Spark 处理的时间消耗、资源占用差异。这样既能证明工具之间的不同又能体现你的工程理解。5. 从单机跑通到工程化交付项目结构、环境与排错5.1 一个可扩展的项目结构毕设项目最忌讳把所有代码堆在一个文件里。推荐按模块拆分成清晰的项目结构car_sales_analysis/ │ ├── app/ # Streamlit 应用 │ ├── main.py # 首页 │ ├── utils.py # 共用读取函数、缓存逻辑 │ └── pages/ # 多页面 │ ├── 1_品牌销量分析.py │ ├── 2_车型销量排行.py │ └── 3_市场趋势分析.py │ ├── crawler/ # 爬虫模块 │ ├── spider.py # 页面抓取 │ └── clean.py # 原始数据清洗 │ ├── spark_jobs/ # Spark 任务 │ ├── preprocess.py # 数据预处理 │ └── analysis.py # 聚合分析 │ ├── data/ │ ├── raw/ # 本地原始 CSV │ └── processed/ # 处理后的 Parquet │ ├── requirements.txt └── README.md项目结构拆得好本身就说明你具备工程化思维。这在答辩时是个隐形加分项。5.2 环境版本匹配最容易被忽视的坑大数据生态的版本兼容问题是很多同学卡壳的第一道门槛。常见问题包括Hadoop 版本和 Spark 版本不兼容。JDK 版本不匹配。Spark 的 Python 版本与项目环境不一致。Hadoop 配置了伪分布式但没有启动 HDFS 相关进程。一个比较稳妥的思路是先查官方文档确定你使用的 Hadoop、Spark、JDK、Python 版本之间的兼容关系再开始安装。不要盲目下载最新版本。启动相关服务时常用命令参考# 启动 HDFS start-dfs.sh # 检查 HDFS 状态 hdfs dfsadmin -report # 创建目录并上传文件 hdfs dfs -mkdir -p /data/car_sales/raw hdfs dfs -put data/raw/raw_sales.csv /data/car_sales/raw/ # 查看 HDFS 文件 hdfs dfs -ls /data/car_sales/raw/ # 提交 Spark 作业 spark-submit spark_jobs/preprocess.py # 启动 Streamlit 应用 streamlit run app/main.py5.3 常见错误排查链路如果页面或者 Spark 作业报错建议按下面的顺序排查不要一上来就改代码先看报错是哪一层出现的是爬虫采集失败、HDFS 文件读不到、Spark 作业运行失败还是 Streamlit 页面无法访问。再看输入数据原始 CSV 是否存在字段名是否和 Spark 代码里一致路径是否写对再看环境HDFS 的 NameNode 和 DataNode 进程是否正常Spark 和 Hadoop 版本是否兼容Python 依赖是否齐全再看资源本地内存是否够用Spark 默认内存配置有没有冲突最后再看代码逻辑在 Spark 作业中可以先show()查看前几行确认数据是否符合预期。一个常见的报错是FileNotFoundError原因往往是 HDFS 路径还没有创建或者本地路径和 HDFS 路径混用。排查时可以先用命令行确认路径存在hdfs dfs -ls /data/car_sales/raw/另一个常见问题是 Streamlit 页面能打开但没有任何图表。这种情况优先检查数据文件是否成功生成以及st.cache_data缓存的是不是旧版本数据。如果重新生成了 Parquet 文件建议先清除缓存再刷新页面。5.4 答辩加分点从“能跑”到“能讲清楚”很多同学的毕设能跑通但答辩时只能说“这个按钮会刷新图表”。这远远不够。要想讲得清楚可以从这几个方面准备画一张数据流架构图爬虫 → HDFS → Spark → Parquet → Streamlit每层标注使用的工具和处理内容。解释每一步的职责和为什么这样设计。准备一个“如果数据规模扩大”的扩展方案HDFS 加节点、Spark 切换集群模式、Streamlit 部署到服务器。准备一个实际分析结论例如某品牌在某价格区间销量最高背后的可能原因是什么。答辩时一个能讲清“数据从哪里来、经过什么处理、得到什么结论、为什么可信”的同学远比只会操作页面的同学更有优势。6. 适用边界什么人适合这个选题什么时候该绕开它6.1 适合谁和不适合谁这个选题适合以下情况计算机、软件工程、大数据、数据科学等相关专业。想重点展示“大数据处理链路”的同学。对 Python 有一定基础愿意学习 Hadoop 和 Spark 基础操作。准备走大数据研发、数据工程方向想借毕设积累项目经验。但它不一定适合所有人如果前端交互要求特别高例如复杂动态图表、拖拽组件Streamlit 的灵活度有限更适合换用 Flask ECharts 或 Vue 全家桶。如果数据量只有几百条又不想花时间搭建大数据环境完全可以用 Pandas 单独完成不必强行引入 Hadoop 和 Spark。如果目标是做一个生产级实时分析系统Streamlit Hadoop Spark 的组合也需要额外补充大量工程能力不能直接照搬毕设方案。6.2 最常翻车的四个环节从经验来看这类系统最容易在以下环节出问题数据量太小分析维度又少最后图表看起来像“为了画而画”。爬虫字段不规范导致后续清洗工作量巨大甚至影响分析结果。环境折腾太久整个项目一半时间都花在 Hadoop 和 Spark 的安装配置上。展示层没有做好聚合直接把几十万条原始数据扔给 Streamlit页面卡到无法接受。针对这些问题建议在项目开始时先定一个“数据字典”把字段、类型、含义、示例值写清楚再进行采集和开发。数据字典虽然简单但能避免开发过程中反复返工。6.3 一个可复用的三步实施框架如果从零开始做这个选题我更建议按下面三个阶段推进第一阶段跑通。用一条链路完成最小验证爬到一个数据文件 → Pandas 清洗 → Streamlit 展示。这个阶段不追求完美目标是确认整条链路的可行性。第二阶段优化。把清洗和聚合逻辑迁移到 Spark把原始数据放到 HDFS输出改成 ParquetStreamlit 加上多页面和缓存。这个阶段体现大数据处理能力。第三阶段工程化。模块拆分、配置文件化、异常处理、日志输出、README 完善。这个阶段体现代码质量和可维护性。这个三步法不只在汽车销售分析系统里适用。换成电商销售分析、视频播放量分析、招聘岗位分析框架完全一样先跑通、再优化、最后工程化。技术栈可以换思路不会变。回到最开始那个问题。基于 Streamlit 的国内汽车销售分析系统最有价值的地方不是学会了某一个框架而是通过一个完整的业务场景把数据采集、存储、计算、分析、可视化这些环节串成了闭环。如果你正在为这个题目做准备不要急着写界面不要急着搭集群。先想清楚一个核心问题你的数据从哪来要回答什么业务问题中间要经过哪些处理。这个问题想清楚了选型、架构、代码、论文都会自然长出来。反之如果只是把工具堆在一起最后只会收获一个演示起来很吃力、提问起来更吃力的作品。
返回列表