拓冰建站拓冰建站
首页 / 资讯中心 / 正文

电影票房大数据分析全链路:Python爬虫+Spark+可视化

在大数据毕业设计中电影票房数据分析与可视化属于典型的“数据采集 - 数据存储 - 数据清洗 - 数据分析 - 可视化展示”全链路项目。它覆盖了 Python 爬虫、Hadoop HDFS、Spark SQL、数据库设计和前端图表展示等多个环节既能体现工程能力也方便在论文和答辩中展示完整的数据处理思路。这篇文章会从毕业设计落地的角度把整个系统的模块划分、环境准备、爬虫采集、数据清洗、Spark 统计分析、Flask 后端接口和 ECharts 可视化完整串起来并对常见问题进行排查说明。如果你正在准备计算机毕业设计选题或者想用一份真实可用的数据项目来练习大数据技术栈这篇文章可以帮你建立一条清晰的技术主线。文章会先解释为什么这样选型再给出一套可以直接照着实现的最小系统最后补充排错路径和可扩展方向。所有代码都使用常见库的标准写法落地时结合你自己的包名、路径和数据集调整即可。1. 电影票房数据分析系统的整体架构与模块划分1.1 为什么选择 Python Hadoop Spark 的组合电影票房数据项目通常需要处理三个层面的问题数据从哪来、数据怎么存、数据怎么算。这三个问题对应到技术选型上分别适合用 Python、Hadoop HDFS 和 Spark 来解决。Python 在数据采集阶段的优势非常明显。requests 库可以模拟 HTTP 请求BeautifulSoup 或者 lxml 可以解析 HTML 页面pandas 适合做小规模数据清洗。这个阶段的数据量通常在几万到几十万条级别用 Python 脚本完全能胜任而且代码量少、调试方便。Hadoop 的 HDFS 在这个项目里扮演的是分布式存储角色。当爬虫采集的数据量达到一定规模或者你需要体现大数据存储和容错能力时把原始数据和清洗后的数据落到 HDFS 上比只存 MySQL 更有说服力。同时 HDFS 也是 Spark 读取数据的常见来源可以让论文里的技术链路形成闭环。Spark 负责真正的批量分析计算。和 pandas 单机处理不同Spark 可以把同一套 DataFrame 算子分布到集群多个节点执行。即便在单机伪分布式环境下Spark 的 DataFrame API 和 SQL 风格也能让数据分析代码更接近生产环境写法这是毕业设计里很好展示的一点。三者的配合关系是Python 爬虫采集数据并做初步清洗结果落到 HDFS 和 MySQLSpark 从 HDFS 读取数据完成多维度统计Web 后端读取统计结果通过 ECharts 展示。这样每个组件都承担了明确职责不会出现“Spark 只用来打印日志”这类尴尬情况。1.2 系统模块与数据流向整个系统可以拆成四个核心模块按照数据流向串联起来。第一个模块是数据采集层。Python 脚本按固定频率或者一次性批量爬取电影票房相关公开数据包括电影名称、上映日期、单日票房、累计票房、导演、主演、评分等字段。采集后先做去重、类型转换和空值处理输出标准化 CSV 文件。第二个模块是数据存储层。清洗后的 CSV 文件同时写入 MySQL 和 HDFS。MySQL 用于 Web 可视化模块直接查询HDFS 用于 Spark 做深层次分析。生产环境里还可以把 HDFS 上的数据注册到 Hive 表进一步用 Hive SQL 分析但在毕业设计最小系统中可以不引入 Hive。第三个模块是数据分析层。Spark 读取 HDFS 上的数据后使用 Spark SQL 或者 DataFrame API 计算总票房排行、年度票房趋势、类型票房分布、评分与票房关系等指标。分析结果导出为 CSV 或 JSON供可视化模块读取。第四个模块是可视化层。Flask 提供 JSON 接口前端页面使用 ECharts 渲染折线图、柱状图、饼图。这一层不需要处理原始数据只负责把分析结果直观展示出来。整个链路的数据流可以概括为爬虫 - CSV - MySQL/HDFS - Spark 分析结果 - Flask 接口 - ECharts 图表。设计时每一层都尽量解耦比如爬虫挂了不影响可视化Spark 分析结果可以预先计算好避免 Web 接口承担重计算任务。1.3 学习环境与生产环境的差异这个系统在毕业设计场景下通常运行在一台 Windows 或 Linux 的虚拟机上。Hadoop 使用伪分布式模式Spark 使用 local 模式或者 standalone 模式MySQL 使用本机安装。这种配置的目的是先让系统跑通验证代码逻辑和数据处理流程。生产环境则完全不同。Hadoop 需要多节点集群NameNode 和 DataNode 分离部署Spark 需要 YARN 或 Kubernetes 做资源调度爬虫需要定时调度和失败重试机制MySQL 需要主从备份Web 服务需要部署在 Nginx 后面。这些内容论文里可以写但实现阶段不建议一开始就追求完整生产架构否则会被集群配置淹没反而忽略了数据分析本身。有一项工作从学习环境到生产环境都必须做就是数据质量检查。无论数据是爬来的还是下载的开源数据集都要先确认字段类型、空值比例、重复记录数、时间范围。如果原始数据本身就乱后面所有分析结果都不可信。2. 环境准备Hadoop、Spark 与 Python 依赖2.1 Hadoop 环境准备Hadoop 在毕业设计中通常以伪分布式模式运行。所谓伪分布式就是在一台机器上同时启动 NameNode、DataNode、ResourceManager、NodeManager 这几个进程模拟一个最小规模的 Hadoop 集群。它的作用不是体现性能而是让你体验 HDFS 的文件上传、下载和目录管理流程。安装 Hadoop 前需要确认 JDK 环境。Hadoop 3.x 需要 JDK 8 或 JDK 11建议先执行java -version确认版本。然后解压 Hadoop 安装包并配置以下核心文件。core-site.xml配置 NameNode 地址configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property /configurationhdfs-site.xml配置副本数和 NameNode 数据目录configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/opt/hadoop/data/namenode/value /property property namedfs.datanode.data.dir/name value/opt/hadoop/data/datanode/value /property /configuration单机伪分布式环境副本数必须设置为 1否则 DataNode 数量不足时HDFS 会进入安全模式文件写入会报错。配置完成后首次启动前需要格式化 NameNodehdfs namenode -format注意格式化操作会清空 NameNode 上的元数据不要频繁执行。如果集群已经正常运行再次格式化会导致原有 DataNode 数据不匹配。启动 HDFS 和 YARNstart-dfs.sh start-yarn.sh使用jps命令检查进程jps正常会看到 NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager 五个进程。缺哪个进程就去对应日志目录排查日志路径通常在 Hadoop 安装目录的logs/下。HDFS 建目录并上传文件hdfs dfs -mkdir -p /input hdfs dfs -put movie_box_office.csv /input/ hdfs dfs -ls /input这里需要注意HDFS 的路径和 Linux 本地路径不是一回事。写代码时要明确区分hdfs://localhost:9000/input/...和/home/user/data/...否则 Spark 读取时经常会出现路径错误。2.2 Spark 环境准备Spark 安装相对简单因为它是计算框架不依赖 Hadoop 的 YARN 调度器也能以本地模式运行。如果你只需要跑通分析流程可以先用 local 模式如果想在论文中体现集群调度再考虑 standalone 或 YARN 模式。下载 Spark 后配置环境变量export SPARK_HOME/opt/spark export PATH$PATH:$SPARK_HOME/bin启动 Spark 后可以打开 Web 界面查看任务状态默认地址是http://localhost:4040。这个页面在 Spark 程序运行时会显示 Job 列表、Stage 数量和每个算子的执行耗时对排查性能问题很有用。最常用的启动方式是编写 Python 脚本通过spark-submit提交执行。示例命令spark-submit \ --master local[2] \ --name MovieAnalysis \ /home/user/analysis.py--master local[2]表示使用本地模式分配 2 个 CPU 核心给这次任务。在虚拟机环境里核心数不要设置过大否则容易把内存占满。学习环境中Spark 和 Hadoop 的版本需要匹配。Spark 官方文档会标注兼容的 Hadoop 版本例如 Spark 3.3 可以搭配 Hadoop 3.3.x。如果版本差距过大Spark 读取 HDFS 时可能出现协议不兼容的报错。2.3 Python 依赖与虚拟环境Python 爬虫和 Web 服务建议使用虚拟环境管理依赖避免和系统 Python 环境冲突。这里推荐使用 venvmkdir movie-project cd movie-project python -m venv venv source venv/bin/activate主要依赖如下pip install requests beautifulsoup4 lxml pandas flask如果需要从 MySQL 读取数据还需要安装 PyMySQL如果 Spark 使用 Python 开发则要确保pyspark包和 Spark 安装版本一致。pip install pyspark3.3.0 PyMySQL依赖安装完成后可以用以下命令快速验证关键库是否可用python -c import pandas; print(pandas.__version__) python -c import pyspark; print(pyspark.__version__) python -c import requests; print(requests.__version__)在这一步容易踩的坑是 pyspark 包和 Spark 版本不一致导致运行时报错所以尽量使用相同的版本号。另一个常见问题是 Python 3.8 以下版本对 pyspark 支持不完整建议使用 Python 3.8 到 3.10 之间的版本。2.4 环境检查清单在实际开始写代码前建议按顺序走一遍环境检查确认每个环节都正常后再进入开发否则后面的问题很难区分是环境问题还是代码问题。检查项检查命令预期结果JDK 版本java -version显示 JDK 8 或 JDK 11Hadoop 进程jps看到 5 个 Hadoop 相关进程HDFS 可访问hdfs dfs -ls /返回根目录列表不报连接拒绝Spark 版本spark-shell --version或pyspark --version显示 Spark 版本号Python 库pip listrequests, pandas, pyspark 已安装MySQL 服务mysql -u root -p能进入 MySQL 命令行这份清单同样适用于答辩前环境演示。建议把检查结果截图保存方便论文附录使用。3. 数据采集用 Python 爬虫获取电影票房数据3.1 采集目标与合规前提电影票房数据爬虫的采集对象通常是公开的票房统计网站、电影资料网站或者电影票务平台的公开数据页。这里需要先明确一个原则只采集公开可访问的数据采集频率要合理不突破登录验证不绕过反爬机制不用于商业用途。毕业设计用于学习研究同时要在代码中设置合理请求间隔并在文档中声明数据来源。建议在爬虫代码中增加 robots 协议检查。正规网站会在robots.txt中声明哪些路径允许爬取。例如import requests def check_robots(domain): url fhttps://{domain}/robots.txt try: resp requests.get(url, timeout5) print(resp.text) except requests.RequestException: print(无法获取 robots.txt)这不是形式主义。答辩时如果被问到数据来源是否合法这部分说明能体现你的工程素养。3.2 页面分析与请求模拟爬虫的第一步不是写解析代码而是先分析目标页面结构。打开浏览器开发者工具切换到 Network 面板查看页面数据是服务端渲染还是 AJAX 异步加载。如果是服务端渲染直接请求页面 URL 后解析 HTML 即可如果是 AJAX 加载需要找到真正的数据接口地址。常见电影数据页面会返回一个包含电影列表的 HTML 页面每部电影数据在一个条目中包含电影名称、上映日期、票房、评分等信息。可以用 requests 实现基础抓取import requests from bs4 import BeautifulSoup import time 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, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9 } def fetch_html(url): 请求页面并返回 HTML 文本 try: resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.text except requests.RequestException as exc: print(f请求失败: {url}, 错误: {exc}) return None def parse_movie_list(html): 解析 HTML 中的电影条目返回字典列表 if not html: return [] soup BeautifulSoup(html, lxml) items [] for block in soup.select(.movie-item): title_node block.select_one(.movie-title) box_node block.select_one(.box-office) date_node block.select_one(.release-date) rating_node block.select_one(.rating) if title_node and box_node: items.append({ movie_name: title_node.get_text(stripTrue), box_office: box_node.get_text(stripTrue), release_date: date_node.get_text(stripTrue) if date_node else , rating: rating_node.get_text(stripTrue) if rating_node else }) return items这里.movie-item、.movie-title等选择器是示例实际项目要依据目标页面的具体结构调整。写代码时建议先打印一条解析结果确认字段正确后再全量爬取。请求头User-Agent必须设置很多网站会拒绝不带浏览器标识的请求。更稳妥的做法是准备多个 User-Agent 轮换但这个项目是合规学习场景保持默认即可不需要模拟浏览器指纹。3.3 数据解析与字段清洗HTML 页面里提取出来的字段通常是字符串不能直接用于数值分析。例如票房可能是3.52亿这样带单位的文本评分可能是9.2分上映日期可能是2023-04-28 上映。这些字段必须转换成统一的计算格式。设计一个清洗函数import re def clean_box_office(text): 将 3.52亿 转为以万元为单位的数字 if not text: return None text text.strip() match re.search(r([\d.])\s*亿, text) if match: return float(match.group(1)) * 10000 match re.search(r([\d.])\s*万, text) if match: return float(match.group(1)) match re.search(r([\d.]), text) if match: return float(match.group(1)) return None def clean_release_date(text): 提取日期字符串标准化为 YYYY-MM-DD if not text: return None match re.search(r(\d{4}-\d{2}-\d{2}), text) return match.group(1) if match else None def clean_rating(text): 提取评分数字 if not text: return None match re.search(r([\d.]), text) return float(match.group(1)) if match else None清洗规则是数据分析项目中最关键的部分之一。你可以在爬虫阶段清洗也可以在 Spark 阶段再清洗。这里建议爬虫阶段只做基础格式化把原始字符串保留一份方便后续追溯问题数值字段的值由 Spark 分析时统一转换。清洗完成后把结果合并成 pandas DataFrameimport pandas as pd def build_dataframe(items): df pd.DataFrame(items) df[box_office_wan] df[box_office].apply(clean_box_office) df[release_date] df[release_date].apply(clean_release_date) df[rating] df[rating].apply(clean_rating) return df3.4 异常处理与请求限速爬虫执行过程中会遇到网络超时、响应内容为空、解析结果为空等情况。不能因为一条数据失败就中断整个流程也不能毫无间隔地疯狂请求。可以在主循环中增加重试机制和限速def crawl_pages(start_page, end_page, sleep_seconds2): all_items [] for page in range(start_page, end_page 1): url fhttps://example.com/movies?page{page} html fetch_html(url) items parse_movie_list(html) print(f第 {page} 页: 获取 {len(items)} 条数据) all_items.extend(items) time.sleep(sleep_seconds) return all_itemssleep_seconds设置 1 到 3 秒。不要连续请求同一个网站这会增加网站服务器压力也可能触发网站的反爬机制。采集完成后把 DataFrame 保存为 CSVdf.to_csv(movie_box_office_raw.csv, indexFalse, encodingutf-8-sig)utf-8-sig编码会在文件开头加上 BOM这样用 Excel 打开 CSV 不会乱码同时 pandas 和 Spark 也能正常读取。这是一个很实用的细节。4. 数据落地清洗结果写入 MySQL 与 HDFS4.1 数据库表结构设计电影票房数据落入 MySQL 时需要设计一张或多张表。对于毕业设计场景建议先设计一张电影主表和一张每日票房表。电影主表保存电影的基础信息CREATE TABLE movie_info ( movie_id INT PRIMARY KEY AUTO_INCREMENT, movie_name VARCHAR(255) NOT NULL, release_date DATE, director VARCHAR(255), rating DECIMAL(3, 1), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );每日票房表保存每日票房数字通过 movie_id 关联电影主表CREATE TABLE movie_box_office ( id INT PRIMARY KEY AUTO_INCREMENT, movie_id INT NOT NULL, box_date DATE NOT NULL, box_office_wan DECIMAL(12, 2) NOT NULL, KEY idx_movie_date (movie_id, box_date), CONSTRAINT fk_movie FOREIGN KEY (movie_id) REFERENCES movie_info(movie_id) );拆分两张表的原因在于电影信息和每日票房是两个不同粒度的数据。一张表里如果同时放电影元信息和每天的票房会产生大量重复的电影名称、导演字段不利于数据一致性维护。用外键关联后修改电影信息只需要改电影主表。在毕业设计里DECIMAL类型用于金额字段不要使用FLOAT。浮点数在数据库中存储精度不稳定会出现0.1 0.2 ! 0.3之类的问题虽然票房数量级较大时影响不明显但这是一个需要养成的好习惯。4.2 写入 MySQL用 Python 写入 MySQL 推荐使用pandas.to_sql配合 SQLAlchemy或者使用 PyMySQL 逐条插入。考虑到数据量大时逐条插入效率低这里使用pandas.to_sql更合适。from sqlalchemy import create_engine engine create_engine(mysqlpymysql://root:passwordlocalhost:3306/movie_db?charsetutf8mb4) df.to_sql( namemovie_info, conengine, if_existsappend, indexFalse, chunksize1000 )注意charsetutf8mb4否则中文可能乱码。if_existsappend表示表存在时追加数据if_existsreplace会先删表再写入重复运行脚本时要根据需求选择。写入前要做主键去重。第一次爬完后再次运行时同一部电影不应该重复插入。可以在写入前用 DataFrame 的drop_duplicates去重或者在表上建立唯一索引ALTER TABLE movie_info ADD UNIQUE KEY uk_movie_name_date (movie_name, release_date);这样数据库层面也能挡住重复数据。批量写入时如果数据量超过几万条chunksize参数很有用。它会把写入操作分批执行避免一次性构造超大 SQL 导致内存占用过高。4.3 写入 HDFS 供 Spark 分析HDFS 是 Spark 分析的数据源。把清洗后的 CSV 上传到 HDFS 时可以直接使用命令行hdfs dfs -mkdir -p /movie/input hdfs dfs -put movie_box_office_clean.csv /movie/input/也可以在 Python 脚本中通过调用hdfs命令完成。推荐保持数据文件和命令分离爬虫脚本只负责生成 CSV上传由独立命令或脚本完成这样每一步的职责都很清晰。在实际生产环境中数据量很大时不会一次性覆盖整个目录。建议 HDFS 目录按日期分区存放/movie/input/2026/01/01/data.csv /movie/input/2026/01/02/data.csvSpark 读取时可以直接扫描目录下的所有分区df spark.read.csv(/movie/input/2026/*/*/)这样可以避免未来处理数据时重复全量读取。毕业设计如果数据量不大可以简化成单个目录但目录分层的思想建议在论文中提及。上传完成后用hdfs dfs -cat抽查文件开头几行hdfs dfs -cat /movie/input/movie_box_office_clean.csv | head -20这里需要确认 HDFS 是否显示正确的 UTF-8 中文内容。如果显示乱码说明 CSV 编码和 Spark 读取时的编码参数不匹配需要在下个阶段处理。5. 数据分析用 Spark 完成票房统计与排行计算5.1 Spark 分析任务的整体思路Spark 分析阶段不处理原始 HTML也不负责页面展示它的任务是从 HDFS 读取结构化 CSV完成多维度聚合统计输出结果供可视化使用。整体代码可以分成三步创建 SparkSession 并读取 CSV。对 DataFrame 做字段类型转换和过滤。用groupBy、agg、orderBy完成各类统计。这里使用 Spark DataFrame API 而不是直接写 SQL。原因是 DataFrame API 能获得更好的类型安全和 IDE 自动补全代码可读性也更强。如果你熟悉 SQL也可以把 DataFrame 注册为临时视图用spark.sql()执行两种方式结果一致。5.2 从 HDFS 读取数据并完成清洗读取 HDFS 上的 CSV 文件from pyspark.sql import SparkSession from pyspark.sql.types import StructType, StructField, StringType, DoubleType, DateType from pyspark.sql.functions import col, to_date, sum, avg, desc, year spark SparkSession.builder \ .appName(MovieBoxOfficeAnalysis) \ .master(local[*]) \ .getOrCreate() df spark.read \ .option(header, True) \ .option(encoding, UTF-8) \ .csv(hdfs://localhost:9000/movie/input/movie_box_office_clean.csv)读取这一行代码有三个关键点。第一headerTrue表示 CSV 第一行是列名否则第一行数据会被当成表头字段。第二encodingUTF-8告诉 Spark 使用 UTF-8 解码避免中文乱码。如果你的 CSV 文件是utf-8-sig格式Spark 读取时也能处理因为 BOM 会被跳过。第三csv()默认把所有列都识别为字符串类型。这不是我们想要的结果所以读取后要显式转换字段类型df df.withColumn(box_office_wan, col(box_office_wan).cast(DoubleType())) \ .withColumn(rating, col(rating).cast(DoubleType())) \ .withColumn(release_date, to_date(col(release_date), yyyy-MM-dd))转换字段类型后要再检查一次数据质量df.printSchema() df.show(5, truncateFalse) df.describe(box_office_wan, rating).show()describe能看到每列的最小值、最大值、平均值和标准偏差。如果票房字段出现负数或空值极多说明清洗规则或爬虫解析有问题要回到采集层修复不要在分析阶段硬扛。5.3 核心统计指标与代码实现电影票房分析的核心指标一般包括电影票房总排行、年度票房趋势、电影类型票房分布、评分与票房关系。这里给出两个最有代表性的统计实现。总票房排行按电影分组求和后降序排列top_movies df \ .groupBy(movie_name) \ .agg(sum(box_office_wan).alias(total_box_office)) \ .orderBy(desc(total_box_office)) \ .limit(20) top_movies.show(20, truncateFalse)如果原始数据中同一部电影有多个上映日的票房记录groupBy(movie_name)会把所有记录相加得到累计票房。如果表结构里每条记录本身已经是整部电影的总票房则不需要sum直接groupBy().max()或者orderBy即可。这个逻辑要在论文里写清楚很多同学答辩时被问到这个问题会卡住。年度票房趋势先提取年份再按年份聚合year_trend df \ .withColumn(year, year(col(release_date))) \ .groupBy(year) \ .agg(sum(box_office_wan).alias(year_total)) \ .orderBy(year) year_trend.show()评分与票房关系可以用相关系数df.select( corr(col(rating), col(box_office_wan)).alias(rating_box_corr) ).show()corr返回评分和票房之间的皮尔逊相关系数范围在 -1 到 1 之间。如果接近 1说明评分越高票房越高如果接近 0说明两者相关性较弱。这个指标适合写进论文的分析章节。5.4 分析结果落盘统计结果导出到 HDFS 或本地文件系统供 Web 后端读取top_movies.write.mode(overwrite).csv(/movie/output/top_movies.csv, headerTrue) year_trend.write.mode(overwrite).csv(/movie/output/year_trend.csv, headerTrue)Spark 写入 CSV 默认会生成多个part-*.csv分片文件而不是单个文件。这是分布式计算框架的正常行为。在数据结构上每个分片文件都是完整数据的一部分组合起来才是完整结果。如果你希望结果输出成单个文件可以在代码里使用coalesce(1)top_movies.coalesce(1).write.mode(overwrite).csv(/movie/output/top_movies_single.csv, headerTrue)注意coalesce(1)会把所有数据集中到一个分区适合小数据量场景。大数据量这样做会失去并行度但在毕业设计数据集规模下是可接受的。导出完成后可以通过 HDFS 命令确认hdfs dfs -ls /movie/output/ hdfs dfs -cat /movie/output/top_movies_single.csv/part-*.csv | head -20这里要注意Spark 导出 CSV 时目录结构是top_movies_single.csv/part-00000-xxx.csv文件在目录里面不是目录本身。用-cat指定通配符part-*.csv可以避免读错路径。6. 可视化Flask 提供接口ECharts 展示票房趋势6.1 可视化方案选型Spark 分析结果是静态 CSV可视化需要先通过后端接口暴露数据再由前端图表库渲染。后端选择 Flask 是因为它轻量、易用非常适合毕业设计这种单机部署场景。你不需要搭复杂的 Django 工程一个app.py文件就能提供多个数据接口。前端选择 ECharts它基于 JavaScript配置项丰富支持折线图、柱状图、饼图、散点图等常见类型而且中文文档完善示例很多。相比 matplotlib 服务端渲染图表ECharts 可以做成交互式网页演示效果好。整体请求链路是浏览器加载 HTML 页面 - 页面通过fetch请求 Flask 接口 - Flask 读取 Spark 分析结果并返回 JSON - ECharts 渲染图表。6.2 Flask 接口实现新建app.py实现一个返回年度票房趋势的接口from flask import Flask, jsonify import pandas as pd app Flask(__name__) RESULT_DIR /movie/output def load_csv(csv_path): 读取单文件 CSV 并转成 JSON 可序列化结构 df pd.read_csv(csv_path) return df.to_dict(orientrecords) app.route(/api/year_trend) def year_trend(): 年度票房趋势接口 data load_csv(f{RESULT_DIR}/year_trend_single.csv/part-00000-xxx.csv) return jsonify({success: True, data: data}) app.route(/api/top_movies) def top_movies(): 票房排行接口 data load_csv(f{RESULT_DIR}/top_movies_single.csv/part-00000-xxx.csv) return jsonify({success: True, data: data}) app.route(/) def index(): return app.send_static_file(index.html) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)实际开发时Spark 导出的分片文件名中包含一长串 UUID硬编码文件名不现实。推荐把 Spark 导出结果统一复制到固定目录或者用 Python 遍历目录找到part-*.csv文件import glob def find_part_file(directory): pattern os.path.join(directory, part-*.csv) files glob.glob(pattern) if not files: raise FileNotFoundError(f{directory} 下没有 part 文件) return files[0]这样即使分片文件名变化代码也能稳定读取。Flask 接口先启动后用浏览器访问http://localhost:5000/api/year_trend如果返回 JSON 数据说明接口正常。也可以先用命令行验证curl http://localhost:5000/api/year_trend6.3 ECharts 前端展示前端页面放在 Flask 项目的static/index.html中。页面使用 CDN 引入 ECharts然后通过 fetch 请求后端接口。下面是完整的最小页面示例!DOCTYPE html html langzh-CN head meta charsetUTF-8 title电影票房数据分析/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script style .chart-container { width: 90%; height: 400px; margin: 20px auto; } /style /head body h2 styletext-align:center;电影票房年度趋势/h2 div idyearChart classchart-container/div h2 styletext-align:center;电影票房排行榜 Top20/h2 div idtopChart classchart-container/div script const yearChart echarts.init(document.getElementById(yearChart)); const topChart echarts.init(document.getElementById(topChart)); function loadYearTrend() { fetch(/api/year_trend) .then(res res.json()) .then(res { const data res.data; yearChart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: data.map(d d.year) }, yAxis: { type: value, name: 票房万元 }, series: [{ name: 年度总票房, type: line, smooth: true, data: data.map(d d.year_total) }] }); }) .catch(err console.error(年度趋势接口请求失败, err)); } function loadTopMovies() { fetch(/api/top_movies) .then(res res.json()) .then(res { const data res.data; topChart.setOption({ tooltip: { trigger: axis }, grid: { left: 25% }, xAxis: { type: value, name: 票房万元 }, yAxis: { type: category, inverse: true, data: data.map(d d.movie_name) }, series: [{ name: 总票房, type: bar, data: data.map(d d.total_box_office) }] }); }) .catch(err console.error(票房排行接口请求失败, err)); } loadYearTrend(); loadTopMovies(); /script /body /html页面里两个图表的配置逻辑很清晰。折线图展示年度票房变化趋势柱状图展示 Top20 电影票房排行。inverse: true可以让排行榜第一项显示在图表最顶部。启动 Flask 后浏览器访问http://localhost:5000如果能看到两条图表并正常交互说明整个系统链路已经打通。注意ECharts 使用 CDN 引入需要联网。如果演示环境没有外网需要把echarts.min.js下载到本地static/js/目录后引用否则图表不会渲染。7. 常见问题与排查路径7.1 爬虫采集不到数据现象爬虫脚本运行后没有报错但解析出的列表为空。排查路径打印响应状态码确认请求是否成功。如果返回 404说明 URL 规则不对如果返回 403说明请求被网站拒绝。检查 HTML 选择器。页面结构调整后旧选择器会失效。先保存一份 HTML 到本地在浏览器里用开发者工具查看实际 class 名称。确认数据是不是 AJAX 异步加载。Network 面板里找到 XHR 请求看返回的是 JSON 还是 HTML。检查 User-Agent 和 Accept 请求头是否完整。解决方式调整 URL 或选择器如果数据来自接口直接请求 JSON 接口并处理 JSON而不是解析 HTML。预防建议爬虫代码里增加日志输出每爬一页都记录条数方便快速定位。7.2 Hadoop NameNode 启动失败现象执行start-dfs.sh后NameNode 进程没有出现或 HDFS 进入安全模式。排查路径查看logs/hadoop-root-namenode-*.log确认具体异常。检查core-site.xml中fs.defaultFS是否配置正确。检查hdfs-site.xml中数据目录是否存在且有写权限。如果之前格式化过多次检查dfs.namenode.name.dir和dfs.datanode.data.dir是否对应同一套数据。用hdfs dfsadmin -report查看 DataNode 是否在线。解决方式如果是首次启动先格式化 NameNode如果是重复格式化导致元数据不一致需要清理数据目录后重新格式化。虚拟机上常用一个坑是名称节点存储目录会被残留旧版VERSION文件污染清理后重启即可。预防建议不要在生产集群上随意执行格式化命令。学习环境格式化前先把 HDFS 中的重要数据下载保存。7.3 Spark 任务内存溢出现象运行 Spark 分析脚本时出现java.lang.OutOfMemoryError或任务卡死。排查路径检查数据集是否太大。单机内存不够时先减小数据集规模测试。检查--driver-memory和--executor-memory参数。local 模式下至少要给 Driver 分配充足内存。检查代码中是否有collect()把所有数据拉回 Driver 的操作。大结果集建议用show(20)或take(20)不要全量 collect。检查是否使用coalesce(1)把并行度降到 1导致单节点处理压力过大。解决方式spark-submit --master local[2] --driver-memory 2g analysis.py如果仍不够就需要减少输入数据量或者在groupBy前先过滤掉不必要字段。预防建议开发阶段使用小数据集验证逻辑再切换到完整数据集运行。7.4 中文乱码问题现象Spark DataFrame 中中文字段显示为乱码或者 MySQL 中中文变问号。排查路径用file命令或编辑器检查 CSV 文件编码确认是 UTF-8 还是 GBK。Spark 读取时添加encodingUTF-8参数。MySQL 连接串添加charsetutf8mb4。HDFS 的复制到本地后检查/etc/hadoop/conf/下是否有编码相关配置。解决方式统一编码规范所有环节使用 UTF-8。CSV 文件建议保存为 UTF-8 或 UTF-8 with BOM数据库连接字符串添加字符集参数。预防建议爬虫写出 CSV 时使用encodingutf-8-sigSpark 和 pandas 读取时显式指定编码。7.5 排错链路总览问题现象常见原因检查方式处理建议爬虫返回 403缺少请求头或请求频率过高检查响应状态码、请求头补充 User-Agent增加 sleep 间隔爬虫列表为空页面结构变化或数据为 AJAX查看页面源码、Network 面板更新选择器改抓 JSON 接口HDFS 写文件失败副本数设置过高或安全模式hdfs dfsadmin -report设置dfs.replication1等待安全模式退出Spark 读取中文乱码编码未指定或 CSV 本身非 UTF-8file命令查看编码Spark read 指定encodingUTF-8Spark 内存溢出Driver 内存不足或 collect 全量数据查看 Spark UI 的 Executor 内存减小数据量、增加内存参数、避免 collectFlask 接口返回 404分片文件名不固定或目录错误查看 HDFS 目录结构、glob 匹配遍历目录找part-*.csvECharts 图表空白CDN 未加载或接口请求失败F12 查看 Console 和 Network下载 ECharts 到本地或修复接口地址8. 最佳实践与毕业设计扩展方向8.1 从“能跑通”到“能答辩”的检查清单很多毕业设计做完后功能可以演示但一到答辩就被问住问题通常出在数据处理细节和边界情况上。下面这份清单可以作为答辩前的自检工具。数据层面数据来源是否明确是否在论文中声明。数据总量是多少时间范围是什么。字段类型是否统一空值是否处理。是否存在重复记录如何保证去重。技术链路层面爬虫、存储、分析、可视化四层是否每一层都有可展示的中间产物。Spark 分析结果是否有验证例如和 MySQL 查询结果对比。图表展示的数据是否和统计结果一致。代码有没有保存到 Git 仓库环境能否跨机器复现。提问应对层面为什么用 Spark 而不是 pandas 直接分析。为什么数据要同时存 MySQL 和 HDFS。爬虫遇到反爬怎么处理。数据量扩大 100 倍后系统哪些环节会先成为瓶颈。如何保证分析结果准确。最后两条这类问题可以参照文中提到的思路回答数据量扩大后爬虫需要引入分布式调度HDFS 需要扩展节点Spark 可以从 local 模式切换到 YARN 模式MySQL 需要考虑分库分表Flask 接口需要加缓存。这些方向可以在论文的“展望”章节展开。8.2 可扩展方向电影票房数据分析项目可以做的扩展很多这里按难度递增列出几个方向。第一个方向是加入 Hive。把清洗后的数据注册到 Hive 表使用 Hive SQL 做分析和 Spark SQL 形成对比。这能体现你对数仓工具的理解但会增加安装配置工作量。第二个方向是引入定时调度。使用 Airflow 或者简单的 crontab 定时执行爬虫和分析任务让数据每天自动更新。这样系统从一次性分析变成持续更新的数据服务整体完整度会提高。第三个方向是增加用户交互。在可视化页面加入日期范围筛选、电影类型筛选、点击排行跳转详情等交互功能。这需要前端和后端配合也是一个加分项。第四个方向是引入更多数据源。除了票房数据可以加入豆瓣评分、猫眼热度、电影类型、导演演员等维度进行多表关联分析例如分析不同导演的票房均值、不同类型的评分差异、评分和票房之间的关系等。这种分析更适合在论文中展示“发现”而不是只罗列排行榜。第五个方向是优化 Spark 性能。不改变数据量通过检查执行计划、增加分区、广播小表等方式优化任务耗时然后对比优化前后的运行时间。这个角度既好写又有说服力。选择扩展方向时不要贪多。毕业设计的核心是完成一条清晰、可靠、可解释的技术链路而不是把整个大数据生态都装进去。选一个方向做深比五个方向都停留在演示层面更有价值。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门