news 2026/8/30 9:53:34

汽车销售分析系统:Python爬虫+Hadoop+Spark+Streamlit全链路实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
汽车销售分析系统:Python爬虫+Hadoop+Spark+Streamlit全链路实战

如果你正在为“汽车销售分析”类毕业设计选技术栈,或者想做一个有“大数据味道”的课程项目,却不知道该把 Python 爬虫、Hadoop、Spark、Streamlit 怎么串联起来,那么这篇文章值得看完。

先给一个明确判断:这个项目真正的难点不是单点技术,而是整条链路的贯通。爬虫抓下来数据怎么进 HDFS?HDFS 里的原始数据怎么让 Spark 做清洗和聚合?Spark 算出的结果怎么给 Streamlit 展示?很多同学卡在“每个组件都能跑起来,但放到一个项目里就不知道如何协作”。这篇文章就按这条链路完整拆开讲解,并且给出可运行的代码示例和验证方式。

另外要提前说明:这类系统的完整形态应该是“爬虫采集 + 分布式存储 + 分布式计算 + 交互式可视化”。但在毕业设计或课程设计场景下,你不需要真的搭一套大规模集群,重点是让每个环节的职责清晰、流程能跑通、代码能解释清楚。所以本文采用 Hadoop 伪分布式 + Spark 本地模式 + Streamlit Web 应用的方案,既保留了完整技术栈,又能在单机上完成演示。

1. 这个项目到底解决了什么问题

汽车销售分析是数据分析类毕业设计里非常经典的方向。原因很简单:业务场景明确,数据字段容易理解,分析结果也有实际业务含义。但很多初版方案只是“拿一张 Excel,用 Pandas 算几个数,再用 Matplotlib 画几张图”,这样做不是不行,而是技术层次不够,答辩时很难讲出亮点。

这套基于 Streamlit 的系统,本质上解决的是三个问题。

第一,数据从哪来。用 Python 爬虫解决“没有数据”的尴尬。汽车销量、车型参数、价格区间、品牌排行这些信息,在合法的公开渠道可以看到,但手动复制显然不现实,所以需要一个可复用的采集脚本。

第二,数据量大了之后怎么存储和计算。虽然单机用 Pandas 也能跑,但为了体现大数据处理思路,需要引入 HDFS 做分布式文件存储,引入 Spark 做分布式内存计算。哪怕实际数据量不大,这套设计在架构上已经具备横向扩展的潜力。

第三,分析结果怎么给非技术人员看。传统控制台输出和 Matplotlib 静态图,不适合做成果展示。Streamlit 的价值在于用纯 Python 快速搭建交互式仪表盘,支持下拉筛选、动态图表和实时刷新,非常适合作为最终展示层。

一个比较中肯的建议是:如果你只想要“能交差”的项目,上面任何一个部分都可以更简单;但如果你想在技术报告和答辩中展示“系统工程能力”,这套技术栈的组合会让项目有明显层次感。

2. 系统总体架构与核心技术概念

整个系统的数据流非常清晰,按照“采集 → 存储 → 计算 → 展示”四个阶段划分,每个阶段对应独立技术组件。建议用一张结构图记录在报告里,文字描述如下。

数据采集层负责从目标网站获取汽车销售相关的公开数据,包括品牌销量、车型列表、价格区间、在售状态等信息。采集结果统一保存为 CSV 格式,作为原始数据。这一层主要用到 Python 的 requests 和 BeautifulSoup 库。

数据存储层采用 Hadoop HDFS。采集到的 CSV 文件上传到 HDFS 指定目录,例如/car_sales/data。这样设计的好处是:如果后续采集数据量增长,HDFS 可以通过横向扩展存储节点来解决容量问题,而不是在某台机器上不断加硬盘。

数据处理层采用 Apache Spark。使用 PySpark 读取 HDFS 上的原始文件,完成字段筛选、空值处理、格式转换、分组聚合等操作。最终把处理结果写回本地或者 HDFS 的output目录,供展示层使用。

数据展示层采用 Streamlit。通过 pandas 读取 Spark 处理后的结果文件,利用 Streamlit 的交互组件构建筛选条件,配合内建图表或 Plotly 展示趋势、排行和分布。用户无需了解底层数据逻辑,就能通过浏览器操作整套分析系统。

需要特别解释 Spark 和 Hadoop 的关系,因为这是很多初学者最容易混淆的地方。Hadoop 的核心是 HDFS(分布式文件系统)和 MapReduce(分布式计算框架),而 Spark 是一个独立的内存计算框架,它可以完全不依赖 Hadoop,也可以读取 HDFS 上的数据作为数据源。在本项目中,我们主要使用 HDFS 的存储能力,计算部分用 Spark,所以两者是协作关系,不是替代关系。

至于 Streamlit,它的定位是“数据应用的快速开发框架”。与传统 Flask/Django 写 Web 页面不同,Streamlit 不需要你写 HTML、CSS、JavaScript,只要写 Python 脚本,页面组件会随着脚本执行自动渲染。这对数据分析方向的学生非常友好。

3. 环境准备与前置条件

为了避免在环境上浪费太多时间,建议在开始之前检查以下条件。

操作系统建议使用 Ubuntu 20.04 或 CentOS 7 等 Linux 发行版,Windows 用户建议先安装 WSL2 或者直接在虚拟机上操作,因为 Hadoop 在 Windows 上的配置问题比较多。如果你的项目必须运行在 Windows 上,也可以,但需要额外处理 Hadoop 本地库和 winutils,不建议新手一上来就挑战。

Python 版本建议 3.8 以上,推荐 3.9 或 3.10。Streamlit 和 PySpark 对 Python 版本有各自要求,太新的 Python 版本有时会遇到依赖包尚未适配的情况,所以不要一味追求最新版。

需要提前安装的工具包括:

  • JDK 8 或 JDK 11(Hadoop 运行依赖)
  • Hadoop(建议使用 3.x 版本,伪分布式模式即可)
  • Spark(建议使用 3.x 版本,需提前确认与 Hadoop 的兼容性)
  • Python 虚拟环境工具(venv 或 conda)
  • Git(可选,用于版本管理)

这里不写死具体版本号,因为 Hadoop、Spark、Python 三者之间存在版本兼容矩阵,最稳妥的方式是先确认一组互相匹配的版本,再开始安装。以实际项目环境为准,本文重点演示通用思路。

依赖安装命令可以统一在虚拟环境中执行。先创建虚拟环境:

python3 -m venv car_sales_env source car_sales_env/bin/activate

然后安装 Python 库:

pip install streamlit pyspark pandas requests beautifulsoup4 plotly

如果你的机器无法直连 PyPI,可以配置国内镜像源,但不建议在文章中讨论代理相关的敏感内容。可以提一句“如果下载速度慢,可以配置国内 pip 镜像”,这是很常规和安全的建议。

检查安装是否成功:

streamlit version python -c "from pyspark.sql import SparkSession; print('pyspark ok')"

如果这两个命令都能正常输出,说明基础环境已经准备好了。

4. 数据采集模块设计与实现

在动手写爬虫之前,必须先强调合规问题。爬虫只能采集公开合法、允许访问的数据,且需要遵守目标网站的 robots 协议和访问频率要求,不得通过绕过访问限制、破解验证码等非法手段获取数据。毕业设计项目更要注意这一点,尽量选择提供公开数据接口或明确允许爬取的网站。如果目标网站没有公开接口,也可以用模拟数据代替,把爬虫设计成“数据来源示例”,重点是展示爬虫技术能力,而不是真正破坏某个网站的服务。

从技术实现角度,一个完整的汽车销售爬虫通常包含数据源分析、页面请求、HTML 解析、数据清洗、结果保存五个步骤。

这里演示一个通用章节:假设目标页面是一个展示汽车销量排行榜的公开页面,每行包含品牌、车型、销量、价格区间等字段。用 requests 请求页面,用 BeautifulSoup 定位表格行,解析数据后输出为 CSV。

# 文件路径:spider/car_spider.py import csv import requests from bs4 import BeautifulSoup headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } def fetch_page(url): """请求目标页面,返回 HTML 文本""" resp = requests.get(url, headers=headers, timeout=10) resp.encoding = resp.apparent_encoding if resp.status_code == 200: return resp.text else: raise Exception(f"请求失败,状态码:{resp.status_code}") def parse_sales_table(html): """解析页面中的销量表格""" soup = BeautifulSoup(html, "html.parser") table = soup.find("table") rows = [] if table is not None: for tr in table.find_all("tr")[1:]: cells = tr.find_all("td") if len(cells) >= 4: brand = cells[0].text.strip() model = cells[1].text.strip() sales = cells[2].text.strip().replace(",", "") price = cells[3].text.strip() rows.append([brand, model, int(sales), price]) return rows def save_to_csv(rows, file_path): """保存为 CSV 文件""" with open(file_path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["brand", "model", "sales", "price_range"]) writer.writerows(rows) if __name__ == "__main__": target_url = "https://example.com/public_car_sales" # 替换为实际合法数据源 html = fetch_page(target_url) data = parse_sales_table(html) save_to_csv(data, "car_sales_raw.csv") print(f"采集完成,共 {len(data)} 条记录")

这段代码的关键逻辑有三处。请求头里设置 User-Agent 是为了模拟正常浏览器访问,降低被拒绝的概率;解析时删除销量字段中的逗号,是为了让字符串 “12,345” 能转成整数 12345;保存时使用 utf-8-sig 编码,是为了让生成的 CSV 在 Excel 中打开时中文不乱码。

如果目标页面不是简单的静态表格,而是通过 JavaScript 动态渲染,那么 requests 直接请求拿不到有效内容。更稳妥的做法是使用 Playwright 或 Selenium 模拟浏览器行为,或者在浏览器开发者工具中寻找 XHR 接口,直接请求接口地址。对毕业设计而言,优先推荐找公开接口,运维成本低,代码也更容易解释。

如果由于各种原因没有合适的真实数据源,建议生成一套符合业务逻辑的模拟数据。字段可以包括月份、品牌、车型、销量、指导价、地区等,用 Python 的 random 库生成 5 万条记录,然后导出 CSV。这种方式同样可以演示后续的存储和计算流程。

5. Hadoop HDFS 存储环境与数据上传

数据采集完成后,接下来要解决“把数据放到分布式文件系统”的问题。这里使用 Hadoop 伪分布式模式,即在一台机器上同时模拟 NameNode、DataNode、SecondaryNameNode 多个角色。虽然它并不是真正意义上的集群,但能完整展现 HDFS 的文件上传、下载、目录管理等操作。

启动 Hadoop 之前,需要确认几个关键配置。core-site.xml 中的 fs.defaultFS 要指定为 hdfs://localhost:9000,这样客户端才能通过这个地址访问 HDFS。hdfs-site.xml 中要根据机器内存设置副本数,伪分布式环境下副本数建议为 1,否则会因找不到足够的 DataNode 而一直处于 under-replicated 状态。

格式化 NameNode 是首次启动必做的操作,但要注意,格式化操作会清空原有元数据,所以只在初始化时执行一次,不要在实验过程中随便重复执行。

# 格式化 NameNode(仅在首次初始化时执行) hdfs namenode -format

格式化完成后启动 HDFS 服务:

start-dfs.sh

使用 jps 命令检查进程是否就绪,预期能看到 NameNode、DataNode、SecondaryNameNode 三个进程。如果缺少 DataNode,先查看日志文件排查启动失败原因,常见原因是目录权限问题或端口被占用。

然后创建项目目录并上传爬虫生成的 CSV 文件:

# 创建 HDFS 目录 hdfs dfs -mkdir -p /car_sales/data hdfs dfs -mkdir -p /car_sales/output # 上传原始数据 hdfs dfs -put car_sales_raw.csv /car_sales/data/ # 查看文件是否上传成功 hdfs dfs -ls /car_sales/data/

上传后可以尝试读取文件的前几行,确认数据没有损坏:

hdfs dfs -cat /car_sales/data/car_sales_raw.csv | head -5

这个阶段可能遇到的典型问题是执行 start-dfs.sh 时提示找不到命令。原因通常是 Hadoop 环境变量没有配置,需要检查 ~/.bashrc 中的 HADOOP_HOME 和 PATH。另一个典型问题是 DataNode 无法启动,这通常与第一次格式化后持久化目录冲突有关,解决方式是在确保没有重要数据的前提下,清空 tmp 目录后重新格式化,但生产环境绝不能轻易这样做。

到这里,“采集数据 → 存入 HDFS”的链路已经完成。在毕业设计答辩时,可以重点解释这一步的价值:HDFS 提供了跨节点的文件存储能力,原始数据进入 HDFS 后,后续任何计算节点都可以按照统一路径读取,而不需要关心数据具体存放在哪台机器上。

6. 基于 Spark 的数据清洗与聚合分析

数据进入 HDFS 后,下一步是使用 Spark 进行分析计算。Spark 的优势在于它会在内存中完成多阶段计算,比传统的磁盘读写式 MapReduce 更适合做迭代式分析和交互式查询。在毕业设计场景下,我们可以用 PySpark 编写一个 ETL 任务,从 HDFS 读取原始 CSV,完成数据清洗,再执行典型的聚合分析。

分析任务可以围绕以下几个问题展开:

  • 每个汽车品牌的月销量趋势如何?
  • 哪些车型在总销量榜上排名靠前?
  • 不同价格区间的销量分布有什么规律?
  • 年度总销量 Top10 车型有哪些?

假设原始 CSV 包含 month、brand、model、sales、price_range 五个字段,那么 Spark 代码可以这样写。

# 文件路径:analysis/sales_analysis.py from pyspark.sql import SparkSession from pyspark.sql.functions import col, sum, desc spark = SparkSession.builder \ .appName("CarSalesAnalysis") \ .getOrCreate() # 从 HDFS 读取原始数据,注意编码必须与写入时一致 df = spark.read.csv( "hdfs://localhost:9000/car_sales/data/car_sales_raw.csv", header=True, inferSchema=True, encoding="utf-8" ) print("=== 原始数据 Schema ===") df.printSchema() print("=== 总记录数 ===") print(df.count()) # 清洗:过滤销量为空或小于等于0的记录 df_clean = df.filter(col("sales").isNotNull() & (col("sales") > 0)) # 聚合1:各品牌总销量 brand_summary = df_clean.groupBy("brand") \ .agg(sum("sales").alias("total_sales")) \ .orderBy(desc("total_sales")) print("=== 品牌销量 Top10 ===") brand_summary.show(10) # 聚合2:各价格区间销量分布 price_summary = df_clean.groupBy("price_range") \ .agg(sum("sales").alias("total_sales")) \ .orderBy(desc("total_sales")) print("=== 价格区间销量分布 ===") price_summary.show() # 写入处理结果,供 Streamlit 展示使用 brand_summary.coalesce(1).write.csv( "hdfs://localhost:9000/car_sales/output/brand_summary", header=True, mode="overwrite" ) price_summary.coalesce(1).write.csv( "hdfs://localhost:9000/car_sales/output/price_summary", header=True, mode="overwrite" ) spark.stop()

这段代码里有几个容易出错的地方需要留意。

第一,read.csv 的 encoding 参数必须与爬虫写入时的编码一致。如果爬虫写入是 utf-8-sig,Spark 读取时指定 encoding=utf-8 已经可以正常处理 BOM,但若写入时是 gbk,则要相应修改。

第二,write.csv 时使用 coalesce(1) 是为了将结果合并成单文件,方便后续用 pandas 直接读取。如果不做合并,Spark 会为每个分区生成一个 part 文件,Streamlit 读取时就需要额外处理。对于展示型项目,合并成单文件更省事。

第三,Spark 任务提交后输出非常长,真正需要关注的是日志中最后显示的执行状态。如果出现 ERROR 或 Exception,优先看最底部的错误摘要,而不是滚动查看几千行 INFO 日志。

运行 Spark 任务的命令:

spark-submit analysis/sales_analysis.py

如果你的 Spark 与 Hadoop HDFS 不在同一台机器上,需要把代码中的 hdfs://localhost:9000 换成实际的 NameNode 地址。如果只想在本地模式下测试,也可以把读取路径改为本地 CSV 路径,Spark 同样支持。

运行完成后,从 HDFS 拉取结果到本地:

hdfs dfs -getmerge /car_sales/output/brand_summary ./brand_summary.csv hdfs dfs -getmerge /car_sales/output/price_summary ./price_summary.csv

这一步的意义是最终的 Streamlit 应用不需要依赖 Spark 环境,只需要读取已经计算好的结果文件,这对于毕业设计演示来说更加稳定,也方便在没有 Hadoop 的机器上临时展示页面。

7. Streamlit 交互式可视化大屏制作

Streamlit 是整个系统的门面。前面所有环节都是数据准备,最终用户看到的,是浏览器里的仪表盘页面。Streamlit 的核心思路是“把 Python 脚本的每一次执行结果渲染成界面”,页面上的组件会与脚本变量直接绑定,用户触发控件时脚本会重新执行。

本项目中的仪表盘至少应该包含四个模块:核心指标卡片、品牌销量排行、月度销量趋势、价格区间分布。为了增强可视化效果,可以引入 Plotly 绘制交互式图表,Streamlit 本身也提供 st.line_chart、st.bar_chart 等简单图表接口,但 Plotly 的图表更美观,交互能力更强。

先看整体页面骨架代码:

# 文件路径:app/dashboard.py import pandas as pd import streamlit as st import plotly.express as px st.set_page_config( page_title="国内汽车销售分析系统", page_icon=":car:", layout="wide" ) st.title("国内汽车销售分析系统") st.markdown("基于 Streamlit + Spark + Hadoop 的毕业设计项目示例") @st.cache_data def load_data(): brand_df = pd.read_csv("../output/brand_summary.csv") price_df = pd.read_csv("../output/price_summary.csv") trend_df = pd.read_csv("../output/trend_summary.csv") detail_df = pd.read_csv("../data/car_sales_raw.csv") return brand_df, price_df, trend_df, detail_df brand_df, price_df, trend_df, detail_df = load_data()

使用 st.cache_data 装饰器是为了缓存数据加载结果,避免每次页面交互都重新读取 CSV 文件。这是 Streamlit 性能优化的关键手段,否则当数据文件几百兆时,页面响应会明显变慢。

接下来构建侧边栏筛选器。在真实分析场景中,我们经常需要按品牌、价格区间筛选后再查看图表。Streamlit 的多选组件可以方便地实现这个功能:

st.sidebar.header("筛选条件") brand_options = detail_df["brand"].unique().tolist() selected_brands = st.sidebar.multiselect("选择品牌", brand_options, default=brand_options[:5]) filtered_df = detail_df[detail_df["brand"].isin(selected_brands)]

然后展示核心指标卡片:

col1, col2, col3, col4 = st.columns(4) col1.metric("数据总记录数", f"{len(detail_df):,}") col2.metric("品牌数量", f"{detail_df['brand'].nunique():,}") col3.metric("车型数量", f"{detail_df['model'].nunique():,}") col4.metric("总销量", f"{filtered_df['sales'].sum():,}")

st.columns 是 Streamlit 的布局组件,可用于在一行中排列多个模块。st.metric 组件适合展示关键指标,标题下方会显示数值,也可以增加 delta 参数表示变化量。

品牌销量排行可以绘制横向条形图:

fig_brand = px.bar( brand_df.head(10), x="total_sales", y="brand", orientation="h", title="品牌销量 Top10" ) fig_brand.update_layout(yaxis=dict(autorange="reversed")) st.plotly_chart(fig_brand, use_container_width=True)

月度趋势图用折线图展示:

fig_trend = px.line( trend_df, x="month", y="total_sales", color="brand", title="各品牌月度销量趋势" ) st.plotly_chart(fig_trend, use_container_width=True)

价格区间分布用饼图或柱状图:

fig_price = px.pie( price_df, names="price_range", values="total_sales", title="价格区间销量占比" ) st.plotly_chart(fig_price, use_container_width=True)

最后展示明细数据表:

st.subheader("销售明细数据") st.dataframe(filtered_df, use_container_width=True)

这里需要注意,Streamlit 页面中每个图表模块都要通过 st.plotly_chart 或 st.dataframe 输出,不能直接写 Python 表达式,否则页面不会渲染任何内容。如果某个模块不需要展示,可以直接注释掉,不需要像传统 Web 开发一样配置路由。

在展示层面还有一个常见需求:如何把多个总结文件组织在一个页面里。建议按“总览 → 品牌排行 → 价格分布 → 趋势 → 明细”的顺序排列,让用户在浏览时有清晰的信息层次。如果想让页面更专业,可以增加标题、说明文字和分隔线,但不要堆砌过多花哨组件,保持仪表盘的清爽感。

8. 完整启动流程与效果验证

当代码都准备好后,推荐先本地运行一次完整流程,再把各步骤串联成脚本,方便反复复现。

建议的启动顺序如下:

  1. 启动 Hadoop HDFS 服务:
start-dfs.sh
  1. 检查 HDFS 进程和数据文件:
jps hdfs dfs -ls /car_sales/data/
  1. 运行 Spark 分析任务:
spark-submit analysis/sales_analysis.py
  1. 从 HDFS 拉取结果到本地输出目录:
hdfs dfs -getmerge /car_sales/output/brand_summary ./output/brand_summary.csv hdfs dfs -getmerge /car_sales/output/price_summary ./output/price_summary.csv hdfs dfs -getmerge /car_sales/output/trend_summary ./output/trend_summary.csv
  1. 启动 Streamlit:
streamlit run dashboard.py

启动成功后,终端会显示本地访问地址,默认是 http://localhost:8501。在浏览器中打开该地址,可以看到仪表盘页面。

如何验证系统正常?可以从四个层面检查。

数据层面:仪表盘顶部的指标卡片应该显示正确的记录数,如果总记录数与爬虫采集时打印的数量一致,说明数据链路是通的。

图表层面:品牌销量排行图中,条形图应该按销量从高到低排列;价格区间饼图各扇区比例应该符合业务直觉,例如“10-20万”区间通常占比最高。

交互层面:在侧边栏切换品牌筛选后,总销量指标和明细数据表应该同步变化,这说明组件绑定关系正确。

日志层面:终端中没有出现红色报错信息,Streamlit 的页面访问日志会显示 200 状态码。

如果你在浏览器中打开页面但没有看到图表,优先检查输出 CSV 文件是否存在、列名是否与代码中的字段名一致。常见错误是 Spark 写出的文件列名带了双引号或特殊前缀,导致 pandas 读取后字段对不上,这时可以先打印 DataFrame 的列名列表,再用 rename 方法调整。

9. 常见问题与排查方法

根据实际开发中经常遇到的问题,整理成一张排查表,可以放在项目 README 中。

问题现象可能原因排查方式解决方案
Hadoop 启动时找不到命令环境变量未配置echo $HADOOP_HOME 查看结果在 ~/.bashrc 中配置 Hadoop 路径
DataNode 无法启动格式化目录不一致查看 Hadoop 日志清空临时目录后重新格式化,生产环境谨慎操作
Spark 读取中文乱码文件编码不一致检查原始 CSV 编码Spark read 时指定正确 encoding
Spark 聚合结果被分区成多文件默认分区数大于1查看输出目录 part 文件使用 coalesce(1) 合并输出
Streamlit 页面启动后无图表结果文件路径不对检查 pandas 读取路径调整路径为实际输出目录
Streamlit 数据更新不及时缓存没有刷新点击浏览器刷新按钮关闭 st.cache_data 或使用清理缓存功能
爬虫请求被拒绝User-Agent 或访问频率问题查看响应状态码设置合法请求头,降低访问频率
HDFS 存储空间不足文件副本或临时数据过多hdfs dfs -du -h / 查看空间清理无用文件,调大 HDFS 容量

这里需要特别提一下 st.cache_data 的坑。在开发阶段,如果你修改了 CSV 文件内容,页面可能仍然显示旧数据,因为 Streamlit 默认会缓存函数结果。解决办法是在方法上临时不使用缓存装饰器,或者在浏览器中点击 Streamlit 右上角的“Clear cache”按钮。调试完成后再加上缓存,这是开发调试与正式运行的典型差异。

另一个常见坑是文件路径问题。很多同学在 vscode 中直接运行 dashboard.py,但当前工作目录与文件所在目录不一致,导致相对路径找不到 CSV。稳妥的做法是在代码开头根据__file__动态计算绝对路径:

import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) OUTPUT_DIR = os.path.join(BASE_DIR, "..", "output")

这样无论在哪个目录下启动 Streamlit,都能正确定位文件。

10. 工程化建议与毕业设计注意事项

如果你希望这个项目不仅是“跑得通”,还能在评阅和答辩中拿到更好的评价,可以参考以下工程化建议。

目录结构要清晰。建议按数据采集、分析计算、可视化展示、输出结果四类拆分明细目录。例如:

car-sales-analysis/ ├── spider/ │ └── car_spider.py ├── analysis/ │ └── sales_analysis.py ├── app/ │ └── dashboard.py ├── data/ │ └── car_sales_raw.csv ├── output/ │ ├── brand_summary.csv │ └── price_summary.csv ├── docs/ │ └── README.md └── requirements.txt

这种结构的好处是,每个模块的职责一目了然,评阅老师打开项目就能快速定位代码。

代码中要适当添加注释,但不要每行都注释。建议在关键步骤上方写清楚“这一步做什么、为什么这样做”。注释不是解释语法,而是解释设计意图,比如“这里用 coalesce(1) 是为了让 Spark 输出单个文件,方便 Streamlit 读取”就比“合并分区”更有信息量。

安全与合规方面,爬虫代码必须在 README 中注明数据来源,声明“仅用于学习研究”并遵守目标网站规则。涉及 HDFS 操作时,尽量使用普通用户权限,不要用 root 直接启动所有服务。如果项目部署到服务器上,Streamlit 服务需要增加访问控制,最简单的方式是绑定回环地址,或者使用内网部署,不要默认暴露到公网。

大数据量的场景下,不要在 Streamlit 中直接加载全量明细数据。更合理的做法是让 Spark 完成所有聚合,Streamlit 只读取聚合结果,明细数据最多用于局部筛选浏览。这样可以减少内存压力,也符合“计算层与展示层分离”的设计思想。

关于论文和答辩阐述,推荐把整个系统分成五个技术亮点来讲。第一,基于爬虫的自动化数据采集,解决了数据来源问题;第二,基于 HDFS 的分布式存储,解决了原始数据统一管理问题;第三,基于 Spark 的高效分析计算,解决了批量数据聚合问题;第四,基于 Streamlit 的交互式可视化,解决了结果展示问题;第五,整条链路的技术选型具有扩展性,后续可以接入更多数据源、增加算法模型、部署到集群环境。

如果项目中还提到算法或模型,可以补充一个预测模块。例如基于历史销量数据,使用 Prophet 或 ARIMA 模型预测下月销量,然后用 Streamlit 绘制预测曲线。这个扩展会进一步提升项目复杂度,适合有余力的同学。

最后提醒一句:不要把 HDFS 格式化当成常规操作,不要在未确认数据安全的情况下重复执行。毕业设计是学习过程,也是培养工程习惯的过程,安全边界、权限管理和数据备份意识,比跑通代码更重要。

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

从杀兽夺寿系统看游戏奖励结算的幂等与并发设计

看到“只剩半年性命绝境觉醒杀兽夺寿系统”这种小说标题,很多后端工程师的第一反应可能是:这跟技术有什么关系?但如果把它当成一份产品需求来拆,会发现里面藏着一整套游戏后台系统设计题:任务怎么接取、击杀怎么上报、…

作者头像 李华
网站建设 2026/8/30 9:52:00

Expo 完整指南:如何用 React 快速跑通第一个跨端应用

Expo 完整指南:如何用 React 快速跑通第一个跨端应用 【免费下载链接】expo An open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web. 项目地址: https://gitcode.com/GitHub_Trending/ex/expo Exp…

作者头像 李华
网站建设 2026/8/30 9:51:23

行人多摄像头重识别数据集

摘要:行人多摄像头重识别数据集是一个面向智能监控与计算机视觉研究的重要视觉数据集,主要用于研究不同摄像头视角下的行人身份匹配问题。数据集概述行人多摄像头重识别数据集是一个面向智能监控与计算机视觉研究的重要视觉数据集,主要用于研…

作者头像 李华
网站建设 2026/8/30 9:51:13

英伟达129亿美金收购Hugging Face,直播揭秘开源AI为何越来越值钱!

天际资本与开源中国直播:探讨英伟达129亿美金收购Hugging Face8月28日周五晚,天际资本创始人张倩和开源中国CEO徐勇进行了一场直播,主题聚焦于英伟达收购Hugging Face。直播中,评论区问得最多的问题便是收购价,很多人疑…

作者头像 李华
网站建设 2026/8/30 9:50:28

彻底搞懂JS箭头函数与this指向:从原理到工程实践

写 JS 代码的时候,你有没有被 this 坑过? 明明在 setTimeout 回调里只是想访问一下组件实例的数据,结果 this.name 直接给你报 undefined ;明明在事件监听器里想拿到点击的元素,结果 this 指向了 window …

作者头像 李华
网站建设 2026/8/30 9:49:48

从 Vibe Coding 到 Agent 工程:Claude Code 扩展功能新手完整教程

从 Vibe Coding 到 Agent 工程:Claude Code 扩展功能新手完整教程 【免费下载链接】claude-code-best-practice from vibe coding to agentic engineering - practice makes claude perfect 项目地址: https://gitcode.com/GitHub_Trending/cl/claude-code-best-p…

作者头像 李华