尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Streamlit的汽车销售分析系统:从爬虫到可视化的完整大数据项目

基于Streamlit的汽车销售分析系统:从爬虫到可视化的完整大数据项目 每年毕业设计选题季总有同学在“大数据分析”这个方向上犹豫不决。选纯算法方向担心推公式讲不透选Web开发方向又觉得不够有技术含量选爬虫方向又容易做出一个没有任何分析价值的脚本集合。如果你正好处在这样的纠结里基于 Streamlit 构建一个国内汽车销售分析系统是一个值得认真考虑的选项。这个选题最大的优势在于它把 Python 爬虫、Hadoop 存储、Spark 数据处理、Streamlit 可视化完整串成了一条数据链路。你不仅能展示“我会用某个框架”还能向答辩老师讲清楚“数据从哪里来、经过什么处理、最终如何呈现”。这篇文章会从选题价值、系统架构、核心代码实现、验证方式到答辩避坑把这个毕业设计题目的完整开发思路拆开讲清楚。1. 这篇文章真正要解决的问题很多毕业生做大数据分析项目最常见的翻车现场是项目演示时只有几张提前画好的图表老师问“数据怎么更新的”答不上来问“数据处理逻辑在哪里”只能指着一个 SQL 文件说“这就是清洗”。这种项目本质上只是一个“静态可视化页面”谈不上大数据分析。而基于 Streamlit 的汽车销售分析系统核心价值在于它不是一个“画图工具”而是一整套完整的数据管道。通过爬虫获取汽车销量相关数据。把原始数据写入 Hadoop HDFS 做分布式存储。用 Spark 对海量销售记录做清洗和聚合统计。最后通过 Streamlit 提供交互式可视化面板让用户能按品牌、价格区间、地区、时间等维度自由筛选分析。这个选题适合以下人群有一定 Python 基础希望在毕业设计中体现大数据技术栈但不想在研究分布式原理上耗费过多精力的同学。它不需要你精通 Hadoop 源码也不需要你开发复杂的机器学习模型但要求你能把这些开源组件搭建起来、串联起来、把业务问题跑通。这种“全链路”能力恰恰是很多企业招聘时看重的。2. 核心技术栈拆解每个组件真正负责什么项目名字里出现的每个技术名词在实际系统中都有明确的分工不能只是罗列在简历上。2.1 Python 与爬虫负责数据采集。汽车销量数据通常分布在垂直资讯网站、行业数据平台或公开榜单中。使用 Python 的requests、BeautifulSoup、json等库可以定时从这些渠道抓取品牌、车型、销量、价格区间等字段。爬虫是系统的数据入口也是整个链路里最容易出问题的环节因为目标网页结构变化、反爬机制、数据格式不一致都会导致采集失败。2.2 Hadoop HDFS负责原始数据存储。爬虫抓下来的数据是散乱的 JSON 或 CSV 文本如果直接交给 Spark 分析会有大量脏数据。HDFS 在这里扮演的是“数据湖泊”的角色先把原始文件统一上传到 HDFS 指定目录再做后续清洗。在毕业设计环境中通常使用 Hadoop 伪分布式模式单机就能模拟真实 HDFS 的文件读写和副本机制。2.3 Spark负责数据清洗、统计分析和离线计算。它从 HDFS 读取数据完成去重、缺失值处理、格式统一然后按品牌、地区、时间等维度做聚合统计。相比直接用 Pandas 处理Spark 的优势在于当数据量达到百万级以上时它能利用分布式计算能力同一个聚合逻辑可以平滑地从单机扩展到集群。这在毕业设计答辩中是一个非常加分的“工程化设计点”。2.4 Streamlit负责交互式可视化。Streamlit 是一个 Python 开源框架能用纯 Python 脚本快速构建数据应用。它的核心特点是你写一个st.bar_chart刷新页面就是一个图表控件写一个st.selectbox就能生成下拉筛选框。不需要掌握 HTML、CSS、JavaScript几十行代码就能做一个可交互的数据面板非常适合毕业设计这种需要快速交付演示系统的场景。2.5 各技术栈的分工对比组件核心职责输入输出Python 爬虫数据采集公开网页或 API原始 CSV/JSON 文件Hadoop HDFS分布式存储原始文件HDFS 上的数据文件Spark数据清洗与聚合HDFS 上的文件统计结果表MySQL / SQLite结果存储Spark 输出结果结构化结果数据Streamlit可视化交互结果数据浏览器访问的分析面板3. 系统架构与模块划分整个系统的架构可以分成五个层次每一层都有一个清晰的数据入口和出口。数据采集层Python 爬虫定时抓取生成原始数据文件 ↓ 数据存储层原始文件上传 Hadoop HDFS ↓ 数据处理层Spark 读取 HDFS完成清洗和聚合统计 ↓ 结果存储层统计结果写入 MySQL 或 SQLite ↓ 可视化层Streamlit 读取结果库生成交互式 Dashboard在毕业设计论文中建议第一张架构图就画这个五层模型。相比只画一个流程图这个分层模式能清楚展示你对系统整体性的把握同时也是后期写各章分工的骨架。系统内部模块建议按功能拆分几个独立的 Python 包crawler/爬虫模块负责数据采集和本地落盘。storage/负责 HDFS 文件上传和目录管理。spark_jobs/Spark 统计任务一个任务对应一个分析维度。dashboard/Streamlit 页面脚本负责可视化查询。config/存放数据库连接、HDFS 地址、爬虫目标等配置文件。这样拆分的好处是答辩时老师问“你这个系统哪些模块是独立可复用的”你可以直接回答每一个包都可以单独运行、单独测试不是一个所有代码堆在同一个 py 文件里的低质量项目。4. 环境准备与开发环境搭建在开始编码之前先把环境准备好。下面是一套适合毕业设计演示的推荐环境版本号请以官方文档和实际安装为准不要机械照搬。4.1 Python 环境建议使用 Python 3.9 到 3.12 之间的稳定版本。Streamlit 对 Python 版本有一定要求过老的 Python 版本可能无法安装最新版 Streamlit。使用虚拟环境管理依赖避免多个项目依赖互相污染。# 创建并激活虚拟环境 python -m venv venv source venv/bin/activate # Windows 请使用 venv\Scripts\activate # 安装核心依赖 pip install streamlit pip install pandas pip install plotly pip install pyspark pip install requests pip install beautifulsoup4 pip install pymysql pip install apscheduler如果你需要同时连接 Hadoop HDFS在纯 Python 项目中可以使用hdfs库来调用 WebHDFS 接口这比自己实现 Hadoop RPC 协议要简单得多。pip install hdfs4.2 Hadoop 环境毕业设计推荐使用 Hadoop 伪分布式模式。伪分布式是指在单台服务器上以独立进程模拟 NameNode、DataNode、SecondaryNameNode 等角色既能展示 HDFS 的核心机制又不需要多台物理机。启动流程# 格式化 NameNode仅第一次启动时执行 hdfs namenode -format # 启动 HDFS 服务 start-dfs.sh # 验证是否启动成功 jpsjps命令输出中如果看到NameNode、DataNode、SecondaryNameNode三个进程说明 HDFS 核心节点已经正常启动。创建项目数据目录hdfs dfs -mkdir -p /user/student/car_sales/raw hdfs dfs -mkdir -p /user/student/car_sales/clean4.3 Spark 环境Spark 建议使用 Local 模式完成开发调试减少集群资源调度带来的额外复杂度。在提交任务时--master local[*]表示使用本机所有可用 CPU 核心执行任务这个参数对单机演示完全够用。spark-submit \ --master local[*] \ --name CarSalesStatistics \ spark_jobs/sales_statistics.py4.4 数据库环境统计结果存放于 MySQL 或 SQLite。MySQL 更适合展示工程化能力SQLite 更适合快速演示和打包提交。二选一即可重点在于表结构设计清晰。下表是一个简易的汽车销量统计结果表设计字段名类型说明idINT主键自增brandVARCHAR(50)品牌名称modelVARCHAR(100)车型名称sale_dateVARCHAR(20)销售日期sales_volumeINT销量price_rangeVARCHAR(20)价格区间regionVARCHAR(20)销售地区5. 数据采集层用 Python 爬虫获取汽车销量数据爬虫是数据入口也是最容易被忽略工程质量的部分。不少同学把目标网页复制成 HTML 文件然后写一个字符串解析脚本这种方式在答辩中很难自圆其说。更稳妥的做法是先确认目标数据源是否允许爬虫访问遵守robots.txt和网站服务条款在代码中做好频率控制和异常处理仅将数据用于学习和毕业设计研究。下面是一个简化版的采集流程结构上先请求数据再解析字段最后保存到本地 CSV 文件。# 文件路径crawler/car_sales_crawler.py import csv import json import random import time import requests # 演示用模拟接口地址实际项目需替换为确认可合法访问的数据源 API_URL https://example-car-data-api.com/api/v1/sales def fetch_sales_page(page: int, page_size: int 100) - list: 拉取一页销售记录。 实际采集前必须确认目标数据源的服务条款与 robots 规则。 params { page: page, page_size: page_size } headers { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ) } # 注这里会进行真实网络请求本地不能访问外网时 # 可以用下面的 mock_data() 返回演示数据。 resp requests.get(API_URL, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() # 根据返回结构解析记录列表 records data.get(data, {}).get(records, []) return records def mock_data(): 仅用于本地功能演示构造符合字段结构的样本数据。 brands [大众, 丰田, 比亚迪, 本田, 长安, 吉利] models { 大众: [朗逸, 速腾, 帕萨特], 丰田: [卡罗拉, 凯美瑞, RAV4荣放], 比亚迪: [秦PLUS, 宋PLUS, 汉], 本田: [思域, 雅阁, CR-V], 长安: [CS75 PLUS, 逸动, UNI-V], 吉利: [星越L, 帝豪, 博越L], } price_ranges [8-10万, 10-15万, 15-20万, 20-30万] regions [华东, 华南, 华北, 西南, 东北] records [] for _ in range(20): brand random.choice(brands) model random.choice(models[brand]) records.append({ brand: brand, model: model, sale_date: f2024-{random.randint(1, 12):02d}-01, sales_volume: random.randint(1000, 40000), price_range: random.choice(price_ranges), region: random.choice(regions), }) return records def save_to_csv(records: list, file_path: str) - None: 将记录列表写入 CSV 文件。 if not records: return fieldnames [brand, model, sale_date, sales_volume, price_range, region] with open(file_path, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnamesfieldnames) writer.writeheader() writer.writerows(records) def main(): # 在线数据源不可用时切换为 mock_data() 构造本地演示数据 all_records [] for page in range(1, 6): try: records fetch_sales_page(page, page_size100) except Exception as e: print(f请求失败使用模拟数据。错误信息{e}) records mock_data() all_records.extend(records) # 控制采集频率避免对目标站点造成压力 time.sleep(random.uniform(0.5, 1.5)) save_to_csv(all_records, data/raw_sales.csv) print(f采集完成共保存 {len(all_records)} 条记录) if __name__ __main__: main()这段代码中有几个针对答辩的设计点要提前准备第一采集必须带User-Agent并在两次请求之间随机休眠这体现的是对目标站点的礼貌访问也是爬虫的基本素养。第二try...except中切换为模拟数据既能保证代码在离线环境下可运行也能应对真实接口访问失败时系统不崩溃。第三字段名称统一采用英文蛇形命名方便后续 Spark 和 Streamlit 直接读取避免中文字段名在数据管道中引发编码问题。运行方式python crawler/car_sales_crawler.py运行完成后在data/目录下会生成raw_sales.csv文件包含品牌、车型、销量、日期、价格区间和地区字段。6. 数据存储层Hadoop HDFS 上传与目录管理爬虫生成的 CSV 文件还在本地下一步需要上传到 HDFS。这一步在系统里的意义是把“原始数据”与“分析数据”隔离存放避免后面反复读取本地文件产生路径混乱。HDFS 常用命令# 上传本地文件到 HDFS hdfs dfs -put data/raw_sales.csv /user/student/car_sales/raw/ # 查看文件是否上传成功 hdfs dfs -ls /user/student/car_sales/raw/ # 查看文件大小和分块信息 hdfs dfs -du -h /user/student/car_sales/raw/如果你希望爬虫完成之后自动上传文件可以在 Python 中调用 WebHDFS 接口。这里给出一个最小实现把本地上传逻辑封装起来避免每次手动执行 HDFS 命令。# 文件路径storage/hdfs_client.py from hdfs import InsecureClient # HDFS NameNode 地址根据实际部署环境修改 HDFS_HOST http://localhost:9870 HDFS_USER student client InsecureClient(HDFS_HOST, userHDFS_USER) def upload_to_hdfs(local_path: str, hdfs_path: str) - None: 将本地文件上传到 HDFS 指定路径。 local_path: data/raw_sales.csv hdfs_path: /user/student/car_sales/raw/raw_sales.csv client.upload(hdfs_path, local_path, overwriteTrue) print(f文件已上传{local_path} - {hdfs_path}) def list_hdfs_dir(hdfs_dir: str) - list: 列出 HDFS 目录下的文件信息。 return client.list(hdfs_dir, statusTrue) if __name__ __main__: upload_to_hdfs( data/raw_sales.csv, /user/student/car_sales/raw/raw_sales.csv ) print(list_hdfs_dir(/user/student/car_sales/raw))从教学角度看HDFS 部分不需要在论文里展开讲太多底层原理但要能回答三个问题为什么用 HDFS 而不是直接放本地磁盘实际数据存储在哪个节点文件的副本机制是什么能回答这三问就足以证明你不是只知道命令的工具人。7. 数据分析层Spark 清洗与销量统计实现Spark 是整个系统里最能体现“大数据分析”的部分。它承担的任务有两块第一把 HDFS 上的原始数据读进来第二按品牌、时间、价格区间、地区等维度做聚合统计。下面是一个典型的统计任务脚本示例# 文件路径spark_jobs/sales_statistics.py from pyspark.sql import SparkSession from pyspark.sql.functions import sum, col, to_date, year, month from pyspark.sql.window import Window from pyspark.sql.functions import row_number # 初始化 SparkSession本地模式运行 spark SparkSession.builder \ .appName(CarSalesAnalysis) \ .master(local[*]) \ .config(spark.sql.shuffle.partitions, 4) \ .getOrCreate() # 从 HDFS 读取原始销售数据 sales_df spark.read.csv( hdfs://localhost:9000/user/student/car_sales/raw/, headerTrue, inferSchemaTrue ) # 数据清洗去除销量为空或小于 0 的记录 clean_df sales_df.filter(col(sales_volume).isNotNull()) \ .filter(col(sales_volume) 0) \ .dropDuplicates([brand, model, sale_date]) # 增加年份和月份两列便于时间维度分析 clean_df clean_df.withColumn(sale_date, to_date(col(sale_date), yyyy-MM-dd)) \ .withColumn(year, year(col(sale_date))) \ .withColumn(month, month(col(sale_date))) # 统计品牌总销量 brand_sales clean_df.groupBy(brand) \ .agg(sum(sales_volume).alias(total_sales)) \ .orderBy(col(total_sales).desc()) # 统计价格区间销量分布 price_sales clean_df.groupBy(price_range) \ .agg(sum(sales_volume).alias(total_sales)) \ .orderBy(col(total_sales).desc()) # 统计大区销量排名 region_sales clean_df.groupBy(region) \ .agg(sum(sales_volume).alias(total_sales)) \ .orderBy(col(total_sales).desc()) # 查看结果方便调试时确认数据是否正确 brand_sales.show(10) price_sales.show(10) region_sales.show(10) # 写入结果库以 CSV 形式落盘 brand_sales.write.mode(overwrite).csv(data/output/brand_sales) price_sales.write.mode(overwrite).csv(data/output/price_sales) region_sales.write.mode(overwrite).csv(data/output/region_sales) spark.stop()这段代码的关键逻辑在于用dropDuplicates去除同一品牌、车型、日期下的重复记录这是爬虫采集中最常见的脏数据问题。用to_date把字符串日期转成真正的日期类型后续按年、月聚合才不容易出错。所有聚合结果都单独导出到data/output/目录方便 Streamlit 层读取。Spark 是否真的有必要在这个毕业设计里答案是“有必要”因为它定义了系统的扩展边界。答辩时你可以这样说如果数据量从今天的几千条增长到千万条量级只需要把master从local[*]改成 YARN 集群的提交方式同一套聚合逻辑可以直接运行在分布式环境。这种回答比任何背诵的八股文都有说服力。注意如果从 HDFS 读取文件时遇到网络不通、权限不足可以先在本地用 CSV 路径做开发调试确认逻辑正确后再切换回 HDFS 路径。项目开发要有“先跑通、再换环境”的阶段拆分意识。8. 可视化层Streamlit 交互式仪表盘实现Streamlit 是这个项目里开发效率最高的部分。你写一个 Python 脚本保存后刷新页面就能看到图表和筛选器不需要配置任何前端工程。下面是一个完整的 Streamlit 应用框架包含数据加载、侧边栏筛选、指标卡片和三个分析页签。# 文件路径dashboard/app.py import streamlit as st import pandas as pd import plotly.express as px # 页面基础配置 st.set_page_config( page_title国内汽车销售分析系统, page_icon:car:, layoutwide ) st.title(国内汽车销售分析系统) st.markdown(本系统基于 Python 爬虫、Hadoop HDFS、Spark 与 Streamlit 构建数据均为教学演示样本。) st.cache_data def load_data(): 从 Spark 输出的结果目录加载数据。 如果是开发阶段也可以直接读取 data/raw_sales.csv。 brand_df pd.read_csv(data/output/brand_sales/*.csv, headerNone, names[brand, total_sales]) price_df pd.read_csv(data/output/price_sales/*.csv, headerNone, names[price_range, total_sales]) region_df pd.read_csv(data/output/region_sales/*.csv, headerNone, names[region, total_sales]) return brand_df, price_df, region_df brand_df, price_df, region_df load_data() # 侧边栏筛选 st.sidebar.header(筛选条件) all_brands brand_df[brand].tolist() selected_brand st.sidebar.selectbox(选择品牌, [全部] all_brands) all_regions region_df[region].tolist() selected_region st.sidebar.multiselect(选择地区, all_regions, defaultall_regions) # 核心指标卡片 total_sales brand_df[total_sales].sum() brand_count brand_df.shape[0] avg_sales brand_df[total_sales].mean() col1, col2, col3 st.columns(3) col1.metric(总销量, f{total_sales:,}) col2.metric(品牌数量, brand_count) col3.metric(品牌平均销量, f{avg_sales:,.0f}) # 图表区 tab1, tab2, tab3 st.tabs([品牌销量排行, 价格区间分布, 地区销量对比]) with tab1: fig_brand px.bar( brand_df.head(10), xbrand, ytotal_sales, title品牌销量 Top10, texttotal_sales ) st.plotly_chart(fig_brand, use_container_widthTrue) with tab2: fig_price px.pie( price_df, namesprice_range, valuestotal_sales, title价格区间销量占比 ) st.plotly_chart(fig_price, use_container_widthTrue) with tab3: fig_region px.bar( region_df.loc[region_df[region].isin(selected_region)], xregion, ytotal_sales, title地区销量对比, colorregion ) st.plotly_chart(fig_region, use_container_widthTrue) st.markdown(---) st.caption(数据说明本页面展示的统计结果由 Spark 离线计算生成数据文件来自教学演示样本。)8.1 Streamlit 缓存机制st.cache_data是 Streamlit 中一个非常实用的装饰器。默认情况下Streamlit 脚本在每次交互时都会重新执行如果每次都重新读 CSV 或数据库页面会明显卡顿。加上这个装饰器后同样的参数只会加载一次数据后续交互直接走缓存页面响应更快。答辩演示时这个细节体现的是你对数据加载性能的敏感度。8.2 多页签设计使用st.tabs可以把品牌、价格、地区三个分析视角放在同一个页面里而不是像传统 Flask 项目那样跳转多个页面。这种设计更符合数据分析师的日常使用习惯进入系统后在一个页面内完成所有维度的探索。8.3 跑通一个最小可运行版本如果 Spark 输出文件还没有生成可以先在load_data()里临时改为读取data/raw_sales.csv用原始数据做图表保证 Streamlit 页面先能跑起来。然后再切换回 Spark 结果文件形成完整的“数据生成到展示”闭环。9. 系统运行效果与答辩演示要点整个系统开发完成后的运行命令分两步先跑 Spark 统计任务再启动 Streamlit 应用。# 第一步执行 Spark 统计任务 python spark_jobs/sales_statistics.py # 第二步启动 Streamlit 可视化服务 streamlit run dashboard/app.py启动成功后终端会输出本地访问地址默认是http://localhost:8501。用浏览器打开后预期看到以下内容顶部标题与系统简介。三个指标卡片总销量、品牌数量、品牌平均销量。品牌销量排行柱状图显示销量最高的前 10 个品牌。价格区间销量占比饼图。地区销量对比条形图并且可以通过侧边栏筛选地区。答辩演示时建议按以下节奏操作每一步都能对应到系统功能打开 Streamlit 页面先展示整体布局说明每一块区域的功能与对应数据来源。在侧边栏切换品牌和地区展示图表的实时联动效果证明系统具有交互分析能力。切换到终端窗口展示 Spark 任务执行日志指出聚合计算的分区数量和完成耗时。打开 HDFS 目录命令展示原始数据文件在分布式文件系统中的存储位置。最后回到项目代码用一两页 PPT 重点展示数据管道五层模型。如果页面出现空白或图表不显示优先检查data/output/目录下 Spark 是否生成了 CSV 文件以及文件路径是否与pd.read_csv中的模式匹配。10. 常见问题与排查思路开发过程中最容易踩的坑按出现频率从高到低排列如下。问题现象可能原因排查方式解决方案Streamlit 无法启动或端口被占用8501 端口已被其他进程占用查看终端报错信息运行netstat -ano | findstr 8501使用streamlit run app.py --server.port 8502更换端口pd.read_csv(data/output/brand_sales/*.csv)找不到文件Spark 输出的文件不是单个 CSV而是目录下的多个 part 文件用hdfs dfs -ls data/output查看目录结构用通配符*.csv匹配或调整 Spark 输出为单文件模式Spark 任务运行内存不足本地 JVM 堆内存设置过小查看 Spark 日志中的 OutOfMemory 信息在提交命令中添加--driver-memory 2gHDFS 上传文件失败NameNode 未启动或权限不足运行hdfs dfs -ls /测试连接重新执行start-dfs.sh检查用户权限CSV 中文读取乱码文件编码与读取编码不一致用文本编辑器查看文件编码格式pd.read_csv中指定encodingutf-8Streamlit 页面图表不联动筛选变量没有传给图表数据源检查侧边栏变量是否在px.bar中作为过滤条件在图表绘制前增加筛选逻辑例如brand_df[brand_df[brand] selected_brand]这里单独说一下 Spark 输出文件的问题Spark 在分布式模式下写入数据时默认会在输出目录下生成多个part-00000文件而不是一个单独的result.csv。如果你希望在 Streamlit 中更方便地读取可以在写入前调用coalesce(1)强制合并为一个分区输出brand_sales.coalesce(1).write.mode(overwrite) \ .option(header, true) \ .csv(data/output/brand_sales)但要注意coalesce(1)会降低数据写入的并行度。真实项目中数据量大时不建议使用这个点也可以在答辩时主动说明展示你对性能与易用性之间权衡的理解。11. 毕业设计最佳实践与避坑指南这个项目虽然技术栈清晰但如果不注意工程规范很容易在开发过程中变得混乱。下面几条建议来自真实开发经验能帮你少走很多弯路。11.1 数据字段统一命名爬虫、Spark、Streamlit 三个环节共享同一套字段结构。建议在项目根目录维护一份README.md记录每个字段的含义、类型和取值范围。比如sales_volume的单位是“辆”price_range只能是预定枚举值。这份文档既是开发备忘也是撰写论文时“数据字典”章节的素材。11.2 分阶段推进不要一次做完建议按以下顺序推进项目第一阶段用模拟数据跑通 Streamlit 页面和图表交互。 第二阶段编写 Spark 统计任务产出统计结果替换 Streamlit 的数据源。 第三阶段实现爬虫采集真实或模拟接口数据。 第四阶段接入 HDFS形成完整存储链路。 第五阶段加入定时调度让系统能自动更新数据。每个阶段都有可交付、可验证的成果避免最后阶段才发现链路不通、无从排查。11.3 结果数据落库而不是反复计算Spark 计算完成后把聚合结果写入结果表或输出文件。Streamlit 页面只读取结果不参与大数据计算。这样设计的好处是页面响应速度与数据量解耦不会因为原始数据增大而让 Dashboard 变慢。11.4 善用定时任务让数据自动更新如果希望在答辩前展示系统的“数据更新能力”可以用APScheduler实现定时采集和定时分析# 文件路径scheduler/update_task.py from apscheduler.schedulers.blocking import BlockingScheduler from crawler.car_sales_crawler import main as crawler_main from spark_jobs.sales_statistics import main as spark_main def daily_update(): # 先采集数据 crawler_main() # 再将数据上传 HDFS # 最后执行 Spark 统计任务 spark_main() if __name__ __main__: scheduler BlockingScheduler() # 每天凌晨 2 点执行更新任务 scheduler.add_job(daily_update, cron, hour2, minute0) scheduler.start()这个模块放在系统中等于给项目增加了一个“定时数仓更新”的场景答辩时是一个明显的加分项。11.5 代码注释和日志规范化项目源码中核心函数必须写 docstring关键步骤要有日志输出。建议使用 Python 的logging模块把爬虫采集量、Spark 处理耗时、Streamlit 启动日志都记录下来。这不仅是良好代码习惯也方便你在论文里展示系统可观测性。12. 总结与后续提升方向基于 Streamlit 的国内汽车销售分析系统本质上是一个“大数据分析技术的完整落地案例”。它把 Python 爬虫、Hadoop HDFS、Spark、Streamlit 这些技术名词串成了一条真实可运行的数据管道既能作为毕业设计选题也是一份很好的大数据应用开发入门项目。后续如果还有时间可以从以下方向继续扩展把 Streamlit 部署到服务器通过 Nginx 反向代理让其他人可以通过公网地址访问系统接入更多维度的外部数据分析让图表更有业务意义将 Spark 计算从本地模式迁移到 YARN 集群验证分布式环境下的计算性能引入定时调度框架让整个数据管道每天自动运行。技术路线没有唯一答案。有人用 Flask 做可视化有人用 Hive 做数据仓库也有人用 ClickHouse 做实时查询但核心思想是一致的数据采集、存储、计算、展示必须是一套完整链路。你能把这条链路讲透、跑通这就是一个高质量的毕业设计。
返回列表