
说起计算机毕业设计这几年最热门的赛道肯定绕不开大数据方向而 Hadoop Spark Python 这套技术栈更是被各位导师反复点名。如果你正好在纠结选什么题目我强烈建议你看看“起点小说网数据可视化分析系统”这个思路爬虫从起点小说网抓取真实的小说数据交给 Hadoop 做分布式存储再用 Spark 做分布式计算最后用 Python 的 Flask 框架配合 ECharts 做一个可视化大屏。整套链路下来大数据毕设该有的“分布式存储、分布式计算、数据采集、数据展示”全都有了而且数据源是真实网站演示效果非常直观。这个系统到底怎么从零做起每一步会踩哪些坑我把它拆开揉碎讲一遍不管你是计算机专业准备毕设的本科生还是想快速入门大数据全链路的开发者这篇文章都能提供一个可以直接参考复现的完整方案。1. 项目整体设计与思路拆解1.1 为什么选“起点小说网”作为毕设的业务场景很多同学一上来就想着用电商、金融那种高并发场景做大数据分析结果数据拿不到反爬又特别凶做了一半卡在那里。起点小说网这个场景是我实测下来性价比很高的选择。先说数据特征。起点小说的书籍列表页和详情页里包含书名、作者、分类玄幻、都市、科幻等、字数、点击量、推荐票、月票、收藏量、简介、连载状态、更新时间等十几个结构化字段。这些字段做统计分析时非常顺手随便拎出来几个都能做出一张有业务含义的图表而且小说网站的字段标准程度高清洗成本比评论数据低很多能让你把主要精力放在大数据处理链路上而不是和脏数据死磕。再说爬取难度。相比某些电商平台“验证码滑块风控”三件套起点书库页的反爬机制相对基础常规的请求头伪装加合理频控就能稳定拿到数据。爬下来的数据量也很可观一次全量抓取能拿到几千本到几万本小说的元数据这个量级用单机 Pandas 处理起来有点吃力但放进 HDFS 和 Spark 分布式环境里跑就刚刚好能真实体现出分布式计算的价值。最后是可视化效果。小说网站的指标天然适合做成大屏分类占比饼图、点击量 Top10 柱状图、作者作品数条形图、字数与推荐票关系散点图这些图表组合起来视觉效果非常抢眼答辩演示时一眼就能让评委看懂系统做了什么完全不需要你费口舌解释业务背景。1.2 技术栈选型为什么非要用 Hadoop 和 Spark毕设选题里最大的坑就是写着“大数据系统”结果代码全部用 Pandas 在单机上跑完了答辩时导师问一句“你哪里用了分布式”现场直接冷场。为了避免这个问题技术栈的选型要提前想明白每一层承担的角色。这套系统我推荐的分工是这样Python负责爬虫采集数据、写 PySpark 分析脚本、开发 Flask 可视化后端是串联整个系统的胶水语言。Hadoop HDFS存原始爬取数据和中间结果体现分布式文件存储。Hadoop YARN做 Spark 任务的资源调度让 Spark 分析任务跑在集群模式下。Spark读 HDFS 上的数据做分布式清洗、聚合、统计计算核心的分析指标全部在这里产出。MySQL 或本地文件存储分析结果给可视化界面提供查询数据。Flask ECharts提供后端接口和前端图表的可视化展示。每一个组件都有明确的存在理由。Hadoop 解决“数据放在哪、任务怎么调度”的问题Spark 解决“数据怎么算得快”的问题Python 解决“数据从哪来、结果怎么展示”的问题。三者缺一不可也分别对应了大数据课程里最核心的知识模块HDFS、MapReduce/YARN、Spark RDD/DataFrame。为什么不干脆只用 Python Pandas 一步到位因为单机内存有限数据量一大就容易 OOM而且无法体现“大数据处理”的完整链路。当然我也要实话实说如果只是抓了几千本书单机确实也能跑但毕设考察的是你对整个技术生态的理解用 Spark 不是为了炫技而是为了展示你在面对海量数据时的工程化思维。版本选择上建议用兼容性比较稳的组合Hadoop 3.3.x、Spark 3.2.x 或 3.3.x、Python 3.8/3.9。这几个版本之间配合成熟网上踩坑资料多真出问题也容易查到解决方案不要一上来就追最新版本。1.3 系统整体架构与技术链路整个系统的数据流是单向的理解这条链路就理解了这个项目爬虫采集起点小说数据 → 数据清洗与格式统一 → 上传 HDFS 存储 → Spark 读取 HDFS 数据做分布式统计分析 → 结果写入 MySQL 或导出 CSV → Flask 提供数据查询接口 → 前端 ECharts 加载接口数据渲染图表。这个架构是典型的大数据离线处理架构数据不是实时产生的而是爬虫周期性抓取后落盘再触发一次分析任务刷新结果。对毕设场景来说离线分析完全够用而且比实时流处理更容易讲清楚、更容易调试。模块拆分上我建议把整个项目分成四个子模块独立开发爬虫模块spider、大数据存储与分析模块hadoop_spark、后端接口模块server、前端可视化模块web。每个模块单独建目录单独写说明文档这样不管是你自己写代码还是最后写毕业论文里的“系统设计”章节都会轻松很多。2. 起点小说数据爬取把网站数据变成“自己的数据”2.1 爬虫目标分析与反爬应对爬虫阶段的目标非常明确拿到起点小说书库列表页里每本书的字段。常见的做法是抓取列表页因为列表页已经包含了大部分核心指标不需要进入每一本书的详情页既能减少请求次数也能降低被封风险。以起点书库页面为例URL 大致是https://www.qidian.com/all/支持按页翻页和按分类筛选。页面是服务端渲染的 HTML直接分析 HTML 结构就能定位到数据节点。抓取前先看 robots 协议和页面条款不要暴力抓取也不要抓取任何需要登录才能访问的会员专属内容控制在合理频率内。反爬应对上起点比较管用的三个招数请求头伪装。必须带上 User-Agent、Referer、Accept 等常见请求头。User-Agent 可以准备几个常用浏览器的值每次请求随机切换。Cookie 维持会话。第一次访问会种下一些 cookie后续请求带上这些 cookie 会更稳定。随机延时。每次请求之间用time.sleep(random.uniform(1, 3))随机间隔模拟真人浏览节奏。千万别用固定延时固定延时反而容易被规律识别。注意爬虫代码写完后先只抓 1 到 2 页做测试确认字段解析正确后再扩大抓取范围。批量抓取时建议每抓 50 页休息 5 秒钟实测下来稳定性会好很多。2.2 用 requests BeautifulSoup 实现页面解析Python 爬虫我习惯用requests做请求BeautifulSoup做解析这两个库足够轻量新手上手也快。下面是抓取单个列表页并解析所有小说基础信息的核心代码import requests import time import random from bs4 import BeautifulSoup 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, Referer: https://www.qidian.com/, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, } def parse_page(html): soup BeautifulSoup(html, html.parser) books [] # 列表页中每本书对应一个 li可以根据实际 HTML 结构调整选择器 items soup.select(.book-mid-info) for item in items: try: name item.select_one(h4 a).get_text(stripTrue) author item.select_one(.author .name).get_text(stripTrue) category item.select_one(.author).get_text(stripTrue) intro item.select_one(.intro).get_text(stripTrue) # 点击量、推荐票、字数等数据通常在 book-mid-info 后面的 book-right 区域 status item.select_one(.author .new) # 连载状态 books.append({ 书名: name, 作者: author, 分类: category, 简介: intro, 状态: status.get_text(stripTrue) if status else , }) except AttributeError: continue return books def fetch_page(url): resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() resp.encoding utf-8 return resp.text if __name__ __main__: html fetch_page(https://www.qidian.com/all/) result parse_page(html) print(f本页解析到 {len(result)} 本书)这里的核心是 BeautifulSoup 的选择器定位。不同页面的 HTML 结构可能会微调所以写代码前先用浏览器开发者工具手动检查一下目标节点的 class 和层级再写选择器千万不要凭感觉猜。你看到的book-mid-info如果在新页面里不生效就换成实际页面的 class。2.3 多页抓取、数据清洗与本地落盘单页解析没问题后就要写循环翻页逻辑。起点书库页面的翻页参数是?page2这样的形式具体以实际网站为准循环拼接 URL 即可。为了防止重复抓取可以在内存里维护一个已抓取书名的 set遇到重复书名就跳过。抓取完成后数据清洗是很多人容易忽略的一步。原始页面里分类字段往往会把“分类 | 字数 | 连载状态”混在一段文本里需要切分。点击量数字可能带“万”这个单位比如 123.4万需要统一换算成整数。这些清洗逻辑虽然简单但直接影响后续分析的质量建议在爬虫阶段就做好而不是等数据进 HDFS 之后再处理。清洗完之后把结果保存成 CSV 文件。这里有一个细节CSV 编码一定要用utf-8-sig而不是默认的utf-8。utf-8-sig会写入 BOM 头后续用 Excel、Pandas、Spark 读取时都不容易出现中文乱码。import csv def save_to_csv(books, filepath): with open(filepath, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[书名, 作者, 分类, 字数, 点击量, 推荐票, 简介, 状态]) writer.writeheader() writer.writerows(books)等到数据量积累到一定程度比如覆盖完所有分类、几万本书的规模爬虫阶段就算完成。这时候你会得到一份干净的qidian_books.csv后面所有大数据环节都围绕这个文件展开。3. Hadoop 与 Spark 数据分析让数据真正“跑起来”3.1 Hadoop 伪分布式环境搭建要点拿到了数据接下来要把它放进 HDFS。开发调试阶段我没建议大家直接搭三台机器的集群伪分布式模式就足够完成毕设的数据存储和分析需求了。伪分布式意味着所有 Hadoop 进程都跑在同一台机器上虽然做不到真正意义上的多机并行但 HDFS 和 YARN 的完整机制都能跑通学术演示完全够用。Hadoop 安装最关键的一步是配置core-site.xml、hdfs-site.xml和yarn-site.xml三个文件。以 Hadoop 3.x 为例core-site.xml里配置 NameNode 地址hdfs-site.xml里配置副本数和 NameNode 元数据目录yarn-site.xml里配置资源管理相关参数。hdfs-site.xml里有一个特别容易踩坑的参数是副本数dfs.replication。伪分布式只有一台 DataNode如果副本数默认是 3数据块无法复制到 3 份集群会一直处于安全模式文件看起来传上去了但实际上不可用。我用的配置是configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/home/hadoop/data/namenode/value /property property namedfs.datanode.data.dir/name value/home/hadoop/data/datanode/value /property /configuration配置完成后先执行hdfs namenode -format格式化然后执行start-dfs.sh和start-yarn.sh启动服务用jps命令检查NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager五个进程是否都正常。提示格式化 NameNode 之前一定要确认之前跑过一次关闭了所有 Hadoop 进程并且清空了 data 目录。否则容易出现 NameNode 和 DataNode 的 clusterID 不一致导致 DataNode 启动后自动退出这是全网出现频率最高的 Hadoop 启动故障。3.2 Spark 安装与 PySpark 环境配置Spark 安装相对简单下载对应版本的二进制包解压即可。但有一个细节必须处理好Spark 和 Hadoop 的版本兼容性。你在下载 Spark 时要注意它默认依赖的 Hadoop 版本如果你的 Hadoop 是 3.3.x就选spark-3.3.x-bin-hadoop3这种已经预编译好的发行版避免自己编译带来的麻烦。安装完成后要配置环境变量并且关键的一步是告诉 PySpark 使用系统的 Python 解释器。否则 Spark 会去找默认的/usr/bin/python而你的环境里可能装的是 Anaconda 的 Python导致from pyspark import SparkContext直接报错。export SPARK_HOME/opt/spark export PATH$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin export PYSPARK_PYTHON/usr/bin/python3 export PYSPARK_DRIVER_PYTHON/usr/bin/python3配置好之后跑一个最简单的测试验证环境pyspark进入交互式环境后执行spark.range(100).count()返回 100 就说明 PySpark 安装成功。3.3 数据上传 HDFS进入大数据平台的第一步文件准备好了环境也搭好了现在要把qidian_books.csv上传到 HDFS。先创建目录再上传最后查看确认hdfs dfs -mkdir -p /qidian/input hdfs dfs -put qidian_books.csv /qidian/input/ hdfs dfs -ls /qidian/input/上传成功后你可以用hdfs dfs -cat /qidian/input/qidian_books.csv | head -10快速查看文件头部内容确认数据完整。这一步做完数据就正式进入了分布式文件系统后面 Spark 分析都从 HDFS 读取这就能在答辩时很自信地说“我的原始数据是大数据平台统一管理的”。3.4 用 PySpark 做数据统计分析核心代码与思路分析阶段是整个系统的技术核心。我们要用 Spark 分布式计算来完成几类统计整体概况、分类维度分析、作者维度分析、小说指标排行。看一段基于 PySpark 的完整实现思路from pyspark.sql import SparkSession from pyspark.sql.functions import col, desc, avg, sum, count spark SparkSession.builder \ .appName(QidianAnalysis) \ .master(yarn) \ .getOrCreate() df spark.read.csv(hdfs://localhost:9000/qidian/input/qidian_books.csv, headerTrue, inferSchemaTrue, encodingutf-8) print(f共读取 {df.count()} 条小说数据) df.printSchema() # 1. 按小说分类统计数量与平均点击量 category_stat df.groupBy(分类).agg( count(*).alias(小说数量), avg(点击量).alias(平均点击量) ).orderBy(desc(小说数量)) category_stat.show(20) # 2. 点击量 Top10 小说 top_click df.orderBy(desc(点击量)).select(书名, 作者, 点击量).limit(10) top_click.show(10) # 3. 按作者统计作品数与总推荐票 author_stat df.groupBy(作者).agg( count(*).alias(作品数量), sum(推荐票).alias(总推荐票) ).orderBy(desc(作品数量)) author_stat.show(20) # 4. 字数与推荐票的关系相关性分析 corr_value df.select(字数, 推荐票).corr(字数, 推荐票) print(f字数与推荐票相关系数{corr_value})这段代码里初始化 SparkSession 时master(yarn)表示提交到 YARN 集群执行这才能体现 Spark 跑在 Hadoop 资源调度上的完整链路。如果你的 YARN 环境有问题也可以暂时用local[*]模式调试但最终演示时建议恢复成yarn模式。分析结果建议直接保存为新的 CSV 文件方便可视化环节读取category_stat.write.csv(hdfs://localhost:9000/qidian/output/category_stat, headerTrue, modeoverwrite) top_click.write.csv(hdfs://localhost:9000/qidian/output/top_click, headerTrue, modeoverwrite)文件写到 HDFS 后再用hdfs dfs -getmerge /qidian/output/category_stat ./category_stat.csv把分散的 part 文件合并下载到本地。这是 HDFS 输出里非常重要的小技巧因为 Spark 写出的文件是分区多片的直接去读会有几百个小文件getmerge一下才能得到一个干净的汇总文件。4. 数据可视化从统计结果到可视化大屏4.1 可视化方案的选型思路可视化层我强烈推荐 Flask ECharts 的组合。原因有三个Flask 是 Python 的轻量级 Web 框架你本来就用 Python 写爬虫和分析脚本不需要再学一门后端语言整个项目技术栈统一。ECharts 是百度开源的前端图表库图表类型丰富、交互流畅、文档完善中文社区活跃遇到问题很容易搜到答案。把统计结果通过后端接口动态加载到前端能体现“前后端分离”的设计思想这在毕业设计评分里是一个加分项。有人可能会问用 Tableau、FineBI 这种 BI 工具直接做可视化不是更快吗确实更快但毕设的核心是考察你独立开发系统的能力如果整个可视化环节都用商用工具拖拽生成代码量和技术含量都会大打折扣。Flask ECharts 的方案虽然要多写一些代码但每一行都是你自己的工作量答辩时也更有底气。图表和指标的对应关系我列在下面这套组合可以直接照搬指标图表类型业务含义小说分类占比饼图/环形图哪个分类的书最多内容生态结构点击量 Top10柱状图最受欢迎的小说是哪些作者作品数量 Top10条形图哪些作者高产平台内容供给情况字数与推荐票关系散点图字数多少是否影响读者推荐意愿各分类平均点击量横向柱状图哪个分类的平均市场表现更好总体数据卡片数字展示小说总量、作者数、总点击、总推荐4.2 Flask 后端接口开发后端要做的事情很简单读取分析阶段生成的结果文件转成 JSON 返回给前端。下面是一个完整的 Flask 接口示例from flask import Flask, jsonify import pandas as pd app Flask(__name__) # 加载分析结果 category_data pd.read_csv(category_stat.csv) top_click_data pd.read_csv(top_click.csv) app.route(/api/category, methods[GET]) def category(): return jsonify({ categories: category_data[分类].tolist(), counts: category_data[小说数量].tolist(), avgClicks: category_data[平均点击量].tolist(), }) app.route(/api/top_click, methods[GET]) def top_click(): return jsonify({ names: top_click_data[书名].tolist(), clicks: top_click_data[点击量].tolist(), }) if __name__ __main__: app.run(host0.0.0.0, port5000)这里用 Pandas 加载 CSV 是为了让接口代码简洁如果你希望整个链路都保持“大量数据用 Spark”的高姿态也可以把结果导入 MySQL再用 Flask 查询 MySQL。两种方案都可行我倾向于 MySQL 方案因为答辩演示时导师可能问“分析结果如何持久化”你回答“存储在 MySQL 中后端通过接口查询”会更加无懈可击。4.3 前端页面与 ECharts 图表渲染前端页面不需要复杂框架一个 HTML 文件引入 ECharts 的 CDN 就够了。架构上采用页面加载后通过 fetch 请求后端接口动态拿到数据渲染图表。分类饼图的核心代码如下!DOCTYPE html html langzh-CN head meta charsetUTF-8 title起点小说数据可视化分析系统/title script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script /head body div idcategoryChart stylewidth: 600px; height: 400px;/div script fetch(http://127.0.0.1:5000/api/category) .then(response response.json()) .then(data { var chart echarts.init(document.getElementById(categoryChart)); chart.setOption({ title: { text: 小说分类占比 }, tooltip: { trigger: item }, series: [{ type: pie, radius: 60%, data: data.categories.map((name, index) ({ name: name, value: data.counts[index] })) }] }); }); /script /body /html整体大屏可以设计成三栏布局中间是平台总体指标和分类占比左侧是点击量 Top10右侧是作者作品数和相关性散点图。页面顶部放系统标题和小说总量、作者总数等数字卡片底部放各分类平均点击量对比。配色建议用深色背景配合亮色数据这样在大屏上展示时观感最好。运行 Flask 服务后浏览器访问对应的 HTML 页面就能看到数据大屏了。5. 毕设过程中的常见问题与排查实录5.1 环境部署阶段的高频故障这个项目里环境部署的坑远多于业务代码本身。我把实际调试时遇到的问题整理成一张速查表给你参考现象可能原因解决办法jps看不到 DataNodeNameNode 与 DataNode 的 clusterID 不一致停止 Hadoop删除 data 目录重新格式化 NameNodeSpark 提交任务报Python not foundPYSPARK_PYTHON未指向正确解释器在spark-env.sh里显式配置 Python 路径连接 HDFS 失败Connection refusedNameNode 未启动或端口错误确认core-site.xml中fs.defaultFS与代码 URL 一致上传文件后集群处于安全模式副本数设置过大数据块无法复制设置dfs.replication1后重启集群Spark 任务运行超慢提交模式还是local[*]改为--master yarn提交到集群执行分析结果全是小文件Spark 写出时分区数过多写出前用coalesce(1)合并分区或用getmerge合并一个特别容易困惑的点是“Spark 明明在跑为什么看不到 YARN 上任务”。如果用的是local[*]模式Spark 任务只在本地进程里执行YARN 的 Web 界面自然看不到。要确认你的任务真正跑在 YARN 上去看 ResourceManager 的 8088 端口页面能看到对应的 Application 才算成功。5.2 爬虫与数据质量常见问题爬虫阶段最常见的问题是解析结果为空。发生这种情况时不要急着改代码先手动访问目标页面看看页面结构是不是变了。网站改版是很正常的事你代码里写死的 class 选择器可能在某次更新后就失效了。解决办法是重新打开浏览器开发者工具定位最新的节点结构更新选择器。数据清洗阶段容易出现单位不统一的问题。网站展示的点击量可能是“1234”也可能是“1.2万”Spark 在做聚合之前必须统一成数字类型。我建议在爬虫阶段就完成格式化把“1.2万”转成 12000把“万”字全部去掉这样后续分析就省事很多。同样地小说状态字段要统一成“连载”和“完结”两种分类字段要统一成规范分类名。5.3 可视化与答辩演示中的实战建议可视化页面最常见的坑是跨域问题。你用浏览器直接打开 HTML 文件然后 fetch 请求 Flask 接口很可能被 CORS 拦截。解决办法有两个一是 Flask 端配置 CORS 允许跨域二是把 HTML 文件放进 Flask 的templates或static目录用 Flask 直接渲染页面。我推荐第二种不仅解决了跨域问题还能体现前后端整合能力。答辩演示时一定要提前准备好离线演示方案。现场网络不稳定ECharts 的 CDN 可能加载不出来分析接口也可能响应变慢。我的建议是把 ECharts 的 js 文件下载到本地 static 目录接口数据提前缓存一份 JSON 文件万一实时接口出故障还能切到本地数据完成演示。这个细节虽然不起眼但关键时刻能救你一命。6. 答辩要点与后续扩展方向6.1 答辩老师最可能问的几个问题每个老师问问题的风格不同但这个项目的核心问题高度集中提前准备好就不会慌。第一个必问“为什么用 Hadoop 和 Spark而不是直接用 MySQL 和 Pandas”这个问题要突出数据规模和分布式优势回答时强调当数据量增长到 GB 级甚至 TB 级时单机内存无法存放需要用 HDFS 存储并用 Spark 分布式计算能力并行处理。你可以补充一句“本项目的分析逻辑虽然不复杂但技术选型是面向大数据场景设计的”这就把技术含量点出来了。第二个必问“爬虫数据是怎么采集的”回答时要强调遵守访问频率、只爬取公开信息、做了数据清洗和去重。千万不要炫耀自己爬了多快、绕过什么限制要体现出合规意识。第三个必问“你分析出了什么有价值的结论”这里建议准备 2 到 3 个有业务含义的结论。比如玄幻和都市类小说数量最多市场供给集中点击量 Top10 中大部分是连载作品说明连载状态对热度有正向影响字数与推荐票存在正相关说明篇幅较长的作品更容易获得读者推荐。这种结合业务的分析结论比单纯念数据更能体现你的思考深度。6.2 这个项目还能怎么扩展如果时间充裕想要冲击更高分数扩展方向可以从两个维度考虑。从技术维度可以把离线分析升级成实时分析用 Flume 或 Kafka 接入实时数据流再用 Spark Streaming 做流式计算。或者引入 Hive 做数据仓库把原始数据构建成分层数仓模型增加 Hive SQL 的分析环节。这些扩展都能显著提升项目的技术深度。从业务维度可以增加用户评论情感分析用 NLP 技术分析读者对小说的评价并和点击、推荐数据联动形成更立体的分析报告。也可以增加智能推荐模块基于小说的分类、标签和读者行为数据做协同过滤推荐把系统从“分析展示”升级成“分析决策”应用价值会高很多。我在实际做完这套系统后最大的体会是毕设项目真正拉开差距的地方不在于用了多高深的算法而在于你能不能把每一个技术环节讲清楚、跑通、串起来。有些人代码跑通了却讲不明白有些人讲得头头是道但一运行就崩。这个项目只要跟着这条链路老老实实走一遍把每一步的原理都吃透答辩时根本不需要背稿因为每一个细节都是自己亲手调出来的。最后再分享一个小技巧动手写代码之前先把整个流程的文字版走一遍从爬虫到展示每一步的输入输出都写清楚。哪怕最开始只抓 50 本书、用一个最小的数据集把全链路跑通也要比一上来就抓几万本、结果卡在中途强太多。小数据跑通之后再逐步加量你会发现自己对 Hadoop、Spark、Python 的掌控感完全不一样。