如果你正在为毕业设计发愁选题又被Hadoop、PySpark、Scrapy爬虫、知识图谱、推荐系统这一串名词搅得一头雾水那这套酒店推荐系统项目确实值得你认真研究一下。它不是那种只点一下按钮、截几张图就完事的demo而是把大数据领域最常被考察的几个技术点——分布式存储、分布式计算、爬虫采集、图数据库建模、推荐算法——全部落到真实业务场景里的一套完整系统。从爬虫采集酒店数据到Hadoop HDFS存储再到PySpark做离线清洗和特征计算最后把结构化数据导入Neo4j构建酒店知识图谱并基于ALS协同过滤和图谱路径规则生成推荐结果再配合可视化大屏做数据展示整条链路是闭环的。这篇东西我按自己的实战经验来写不堆理论。后面每个环节都会给到你能直接拿去用的代码和配置也会把我在这个项目里踩过的坑单独拎出来说。不管你是打算直接拿这套系统做毕设还是想参考它的思路做一个自己的大数据项目都能从中找到一些真正有帮助的东西。1. 从选题到落地这套系统怎么把大数据全家桶串成闭环1.1 业务场景用户选酒店难在哪系统要解决什么先想清楚业务再谈技术。酒店推荐系统的用户痛点其实就三个信息过载、对比成本高、个性化缺乏。打开任何一个OTA平台酒店数量动辄几千上万条按价格排序、按好评排序这些粗粒度筛选根本无法照顾到每个人的偏好。有的人在乎地段交通有的人只看卫生和服务还有人对品牌房型特别敏感。传统的列表页和搜索框很难把这些隐性需求表达出来。所以这个系统设计了一个很务实的闭环用爬虫把酒店的基础信息、价格、评分、设施、标签、评论数据采集下来存进Hadoop做离线计算构建一个包含酒店、城市、商圈、设施、标签、用户等多类实体及其关系的知识图谱再把用户的历史行为数据和酒店属性喂给推荐模型生成个性化推荐列表最后用一个可视化大屏把数据趋势、酒店分布、推荐结果全部展示出来。这个闭环既能体现大数据技术栈的完整性又能在答辩时讲出一个清晰的“数据如何产生价值”的故事。1.2 六大模块与数据流转路径系统整体分成采集、存储、计算、图谱、推荐、可视化六个模块。数据流向大概是数据采集端用Scrapy定期抓取酒店页面和评论数据产出JSON或CSV格式的原始文件。这些文件不直接进数据库而是先put到HDFS的/raw/hotel目录下按日期分区存放。接着PySpark作业从HDFS读取原始数据做清洗、去重、特征工程把结果写回HDFS的/warehouse目录同时把维度表和评分表同步到MySQL供后端业务查询。知识图谱模块则负责把酒店实体及关系导入Neo4j推荐引擎读取处理好的评分数据和图谱数据用ALS协同过滤和图谱路径规则做混合推荐。最后可视化大屏通过后端接口从MySQL、HDFS统计结果、Neo4j中取数用ECharts渲染大屏页面。数据流是典型Lambda架构的精简版批处理为主实时部分弱化为定时更新。实际做毕设时这个设计完全够用而且比直接上实时流更稳妥因为实时部分一旦出问题整个链路都会卡壳。1.3 技术选型的取舍逻辑很多人在选型时会陷入一个误区什么火用什么完全不考虑场景。这里一定要理解每个组件在这个项目里的不可替代性。Hadoop的核心价值是HDFS的分布式存储和YARN的资源调度。酒店爬虫数据量虽然不大但学习大数据生态时HDFS的副本机制、NameNode管理这些概念是绕不开的考点用它存储原始采集文件能够让整套系统体现出“大数据处理链路”的完整性。PySpark与原生MapReduce相比开发效率高得多。MapReduce写一个WordCount都要几十行而Spark的DataFrame API做清洗、聚合、特征工程非常顺手。尤其这个项目会涉及窗口函数、多表关联、向量化特征处理用Spark能少写一半代码。Neo4j选择图数据库而不是把图谱存MySQL是因为图查询在多跳关系上性能优势明显。比如“找一家和用户住过的酒店共享两个以上标签的新酒店”这种查询在关系表里要多次JOIN在Neo4j里一条Cypher就能跑完。组件本项目用途为什么不用替代方案HDFS原始爬虫文件和计算中间结果存储单机文件系统无法体现分布式存储逻辑PySpark离线ETL、特征工程、指标统计MapReduce开发效率低Pandas撑不起分布式场景Scrapy酒店与评论数据采集Requests手写框架难以应对去重、限速、重试等稳定性问题Neo4j酒店实体关系与图谱推荐MySQL多表JOIN表达多跳关系太笨重MySQL业务查询、用户画像、推荐结果落地团队小数据量关系型存储作为业务库最稳定ECharts大屏可视化社区组件多地图、关系图支持完善选型时一定要能在答辩时回答“为什么用A不用B”这是毕设评分的核心区别点。很多人只摆了技术栈却讲不清选型理由这是很吃亏的。2. Scrapy采集层酒店数据从Web页面到HDFS的完整通道2.1 数据源调研与字段设计爬虫第一步不是写代码而是先想清楚要爬哪些数据、存在什么结构。我当时花了一整天调研几个主流酒店预订平台的页面结构最终确定采集方案爬取几个热门旅游城市下的酒店列表页、详情页和评论页目标数据量控制在城市10个、酒店2000家、评论8万条的量级。这个体量在一台普通开发机上爬几个小时就能完成又足以支撑后续Spark作业、图谱构建和推荐模型训练不会出现“数据量太小显得很假”的问题。字段设计是后面一切计算的地基。我把数据分成三类酒店基本信息hotelId、名称、城市、行政区、商圈、地址、经纬度、星级、品牌、开业时间、装修时间、联系电话酒店经营数据最低价格、最高价格、平均评分、点评数、热度值、房型列表、设施列表、标签列表用户评论数据评论ID、酒店ID、用户ID、评分、评论内容、评论时间、入住类型这里特别要说的是标签字段。我给每个酒店打上“近地铁”“亲子友好”“网红打卡”“商务出行”这类标签虽然在爬取阶段只是几个字符串但后面构建知识图谱时这些标签会成为连接酒店与用户偏好的核心关系。所以采集阶段宁可多存一些看起来用不上的字段也别等做推荐时才发现缺数据。2.2 采集执行中的三个关键问题真正写爬虫时会遇到比教程里复杂得多的问题。第一个是页面结构和反爬。酒店评论通常需要多次点击“展开更多”才能加载完全这种动态页面单纯用Requests拿不到完整数据。我当时的处理方式是列表页和详情页用Scrapy加Requests直接解析HTML评论页则用Selenium模拟浏览器滚动加载之后再抓取。Selenium慢是慢但胜在稳定评论页的控制逻辑简单不怕页面频繁改版。为了降低被封风险我在Scrapy的settings.py里配置了DOWNLOAD_DELAY 2.0即每个请求间隔两秒并启用了随机User-Agent中间件。实测下来这个间隔配合手动构造的请求头基本能保证采集过程不中断。第二个是断点续采。采集过程中很容易因为网络波动、页面结构变化、服务器响应超时报错中断。我在Item Pipeline里做了一套增量记录每条数据写入JSON文件后立即更新一个已采集ID集合下次启动爬虫时通过启动参数判断断点跳过已经采过的详情页和评论页。这个设计虽然简单但在实际项目里省了非常多重爬的时间。第三个是数据一致性。评论页中的评分、价格经常出现“今天起”之类的模糊文本详情页的价格也可能是“每晚XXX起”的促销文案。我在Pipeline里统一做了正则清洗把价格类字段全部转成Float评分类字段保留一位小数城市、商圈等枚举字段做映射标准化。2.3 数据落盘与初步校验爬虫跑完原始数据按城市目录落地到本地再通过Hadoop命令行上传HDFShdfs dfs -mkdir -p /raw/hotel/2025-01-10 hdfs dfs -put ./hotel_guangzhou.json /raw/hotel/2025-01-10/上传完成后我在HDFS上做一个快速校验统计每个分区的文件数量和大小。hdfs dfs -du -h /raw/hotel/2025-01-10这一步很有必要。有些时候爬虫报错了但并没有让程序崩溃数据会缺失一部分肉眼根本看不出来。我在这个项目里就吃过一次亏酒店详情爬了一半评论表正常酒店表少了一个城市的完整数据直接导致后面推荐模型里某个区域的酒店覆盖严重不足。后来我养成了每次采集完都先跑一遍“城市-酒店数-评论数”总量核对脚本的习惯。再补充一个点爬虫文件不建议直接写入HDFS因为Scrapy的存储是流式的一边采集一边写很容易产生大量碎片小文件。更稳妥的做法是采集完成后合并成几个大文件再上传避免HDFS上出现海量小文件拖慢Spark读取。这是我被坑过一次之后得来的经验文本文件看似没区别Spark读小文件时的性能差距非常明显。3. PySpark离线处理从脏数据到可训练特征3.1 环境准备与SparkSession初始化离线计算这块是整个系统的数据中枢。我先说环境。如果毕设环境允许建议在Linux虚拟机里搭一个3节点的Hadoop集群其中一台做NameNode和ResourceManager另外两台做DataNode和NodeManager。内存不够的话退而求其次用伪分布式Spark也用local模式数据链路一样是完整的。我在开发阶段就是伪分布式调试逻辑最后再扔到集群上跑全量数据这样能省很多等待时间。PySpark作业里最容易被忽略的是SparkSession的配置。下面这个配置是我在实际项目中验证过的from pyspark.sql import SparkSession spark ( SparkSession.builder .appName(HotelETL) .master(yarn) .config(spark.sql.shuffle.partitions, 8) .config(spark.default.parallelism, 4) .config(spark.driver.memory, 2g) .config(spark.executor.memory, 4g) .getOrCreate() )spark.sql.shuffle.partitions一定要根据数据量设置。默认200个分区在集群上没问题但在毕设这种小数据量场景反而会增加调度开销。我一般把SQL shuffle分区数设置成核心数的2倍跑出来的性能最稳。executor内存也建议按机器实际内存配置配置过大会导致容器启动失败这是新手最容易踩的坑。3.2 清洗逻辑处理这几类脏数据从爬虫拿到的原始JSON基本不能直接用。我总结下来这批数据通常有四类问题字段缺失很多酒店没有价格、没有评分、没有经纬度需要决定是丢弃还是填充。重复记录同一家酒店在列表页和详情页被采集了多次字段名不一样但hotelId相同。类型错乱价格字段是字符串¥399起评论数是1.2万需要正则清洗。异常值部分酒店价格高达99999元评分超过5分这些属于明显异常值。清洗的代码逻辑我给出核心片段from pyspark.sql.functions import col, regexp_extract, when, sqrt, pow, radians, avg hotel_df spark.read.json(hdfs:///raw/hotel/*/*.json) hotel_df ( hotel_df .dropDuplicates([hotel_id]) .filter(col(hotel_id).isNotNull()) .withColumn( price, regexp_extract(col(price_min), r(\d(\.\d)?), 1).cast(double) ) .withColumn( score, when(col(score) 5.0, None).otherwise(col(score)) ) ) hotel_df hotel_df.fillna({price: 0.0, score: 4.0, lat: 0.0, lng: 0.0})价格正则清洗是关键。酒店平台的价格文案五花八门有的是纯数字有的是“¥328起”有的是“338元/晚”。我用了提取纯数字再加cast的方式统一清洗。评分做了异常值过滤超过5分的直接置空再填充为均值避免极端值干扰后续推荐模型。3.3 特征工程把原始字段变成模型输入清洗完之后进入特征工程环节。推荐系统和可视化分析不是直接吃原始字段而是需要一组经过加工的派生特征。我处理了这么几个地理位置特征用经纬度计算酒店到城市中心的距离分成中心区、近郊、远郊三档。这个特征对城市旅行用户特别有意义很多人选酒店的第一优先级就是位置。hotel_df hotel_df.withColumn( distance_to_center, sqrt( pow((col(lat) - 39.9042) * 111.0, 2) pow((col(lng) - 116.4074) * 111.0 * cos(radians(lat)), 2) ) )价格档位特征把连续价格离散化成经济型、舒适型、高档型、豪华型四档。评分等级按4.5分以上、4.0到4.5、3.5到4.0、3.5以下分成四个等级。标签向量把标签列做one-hot编码这个在ALS模型里会被用到。设施数量统计酒店提供的设施总数作为服务水平的代理特征。特征工程完成后把结果表写回HDFS的分区目录同时把维度表写入MySQL供后端接口查询。写入MySQL我用的是DataFrame的jdbc方法批量写入性能可以接受。3.4 酒店经营分析指标计算除了给推荐模型喂数据离线计算还要输出一套酒店经营分析指标给可视化面板。我批量计算了这些内容各城市酒店数量、均价、平均评分排行不同星级酒店的价格区间分布点评数Top20的热门酒店商圈热度排名按该商圈内酒店的总点评数排序价格带与评分关系分析各价格区间内评分均值和点评总量时间维度趋势按月份统计入住量变化。这些指标全部用Spark SQL直接算。因为数据量不大SQL写起来最直观也不用考虑优化问题。计算结果固化到MySQL的stats表中可视化后端直接读就行。这里有一个实操心得不要在统计指标上搞太多复杂推导。可视化面板最终是要讲给人听的指标之间要有业务上的关联逻辑比如“价格带分析”能和“用户偏好标签”结合起来讲而不是把几十个图表堆在页面上。我见过太多毕设把大屏做成图表堆砌现场数据之间毫无故事线评委看完留下一个“炫技但没逻辑”的印象。4. 酒店知识图谱图模型到底比关系表强在哪4.1 本体设计与关系定义知识图谱是这个项目最出彩的亮点之一也是答辩时最能拉开差距的部分。很多做酒店推荐的毕设都是“MySQL表协同过滤”的组合加入Neo4j图谱后系统的技术含量和创新性一下子就不一样了。构建图谱第一步是本体设计。我定义了六类实体Hotel酒店核心实体承载基础属性和经营数据City城市酒店的地理归属BusinessDistrict商圈酒店所在商圈如“天河商圈”“中关村”Facility设施泳池、健身房、免费停车、24小时前台等Tag标签亲子友好、情侣约会、商务出行等主题标签User用户从评论数据中抽取出用户实体实体之间的关系也做了设计关系语义实例HOTEL_LOCATED_IN_CITY酒店位于城市长隆酒店 - 位于 - 广州HOTEL_IN_DISTRICT酒店属于商圈北京饭店 - 属于 - 王府井商圈HOTEL_PROVIDES酒店提供设施华尔道夫 - 提供 - 室内泳池HOTEL_HAS_TAG酒店拥有标签亚朵酒店 - 拥有 - 商务出行USER_REVIEWED_HOTEL用户评论过酒店用户U001 - 评论过 - 某酒店这个模型看起来简单但设计时有个关键点把“用户”实体放进图谱。很多知识图谱项目只建业务实体忽略用户维度结果图谱只能做展示查询无法和图谱推荐结合。我把用户纳入图谱后推荐时的路径查询才能从“用户的偏好”出发这个设计是后面第5章图谱路径推荐的根基。4.2 数据导入与Cypher查询实战数据导入我用的是Neo4j最成熟的LOAD CSV方案。先把HDFS上的酒店表、标签表、用户评论表导出成CSV拷贝到Neo4j的import目录然后执行Cypher命令创建节点和关系。创建酒店节点的示例LOAD CSV WITH HEADERS FROM file:///hotels.csv AS row CREATE (h:Hotel { hotel_id: row.hotel_id, name: row.name, price: toFloat(row.price), score: toFloat(row.score), star: row.star, city: row.city, district: row.district });创建关系的示例LOAD CSV WITH HEADERS FROM file:///hotel_tags.csv AS row MATCH (h:Hotel {hotel_id: row.hotel_id}) MATCH (t:Tag {name: row.tag_name}) MERGE (h)-[:HAS_TAG]-(t);这里有个容易踩的坑LOAD CSV做关联时一定要先确认两端的节点已经被创建了否则MATCH匹配不到会直接跳过导致关系缺失。我在实践中的做法是先分批次导入所有节点再统一导入关系节点和关系的导入节奏分开控制。图谱建好之后先用几条查询验证正确性。比如查广州酒店数量MATCH (c:City {name: 广州})-[:LOCATED_IN]-(h:Hotel) RETURN COUNT(h);查某个商圈的酒店和共有的标签MATCH (d:BusinessDistrict {name: 天河商圈})-[:IN_DISTRICT]-(h:Hotel) MATCH (h)-[:HAS_TAG]-(t:Tag) RETURN d.name, h.name, COLLECT(t.name) AS tags LIMIT 20;这类查询虽然简单但能快速确认图谱的完整性答辩时现场演示也很有说服力。我建议准备至少三条不同难度的Cypher查询从“单节点查询”到“两跳路径查询”到“带聚合的分组查询”让评委看到图谱是真正可用而不是摆设。4.3 知识图谱在项目里的三个实际用处图谱做出来之后不只是用来展示关系图的。我在这个项目里让它承担了三个实际任务。第一个是辅助推荐系统的冷启动。新用户没有历史行为数据协同过滤完全无法工作。这时候图谱优势就出来了基于用户选择当前酒店或城市用图谱路径规则找到共享标签最多的其他酒店作为推荐候选。这个方案我在第5章会展开讲。第二个是辅助特征构造。图谱中的多跳关系可以派生出大量关系特征比如“该酒店与用户偏好的热门酒店是否同属一个商圈”“该酒店是否提供了用户常选标签对应的设施”这些关系特征拼进推荐特征集能明显提升模型的解释性。第三个是可视化展示。可视化模块里有一个知识图谱探索页面用ECharts的graph类型从Neo4j取数据渲染出以酒店为中心的关系圈用户可以点击关系节点下钻。这个交互功能在答辩演示时视觉冲击力很强也是知识图谱模块“看得见”的价值所在。5. 推荐引擎ALS协同过滤与图谱路径的混合方案5.1 评分矩阵构造与数据分割推荐引擎是整个系统的算法核心。项目里用户的显式评分来自评论中的评分字段我把评论数据按user_id和hotel_id聚合生成评分矩阵。隐式反馈则用“用户是否评论过该酒店”构造在冷启动和稀疏场景下备用。数据处理的核心代码rating_df ( review_df .groupBy(user_id, hotel_id) .agg(avg(rating).alias(rating)) .filter(col(rating).isNotNull()) )评分矩阵的数据分割要特别小心。推荐系统评测用的是时间切分或随机切分毕设场景我用8比2的随机切分就够。但要注意切分后必须过滤掉测试集中新出现的用户和酒店否则评估结果虚高。Spark ALS有一个coldStartStrategydrop参数设为drop后预测时遇到新用户或新物品会自动跳过这个参数必须配置上。5.2 ALS模型训练与调参ALS交替最小二乘是Spark内置的协同过滤算法不需要自己实现矩阵分解。我给的基准参数是rank12、regParam0.08、maxIter15、alpha1.0。from pyspark.ml.recommendation import ALS als ALS( userColuser_id, itemColhotel_id, ratingColrating, rank12, regParam0.08, maxIter15, coldStartStrategydrop ) model als.fit(train_df) predictions model.transform(test_df)训练完之后用RMSE评估from pyspark.ml.evaluation import RegressionEvaluator evaluator RegressionEvaluator( metricNamermse, labelColrating, predictionColprediction ) rmse evaluator.evaluate(predictions)我实际跑下来基础参数的RMSE大约在0.92左右经过调参能降到0.85附近。调参可以用交叉验证但数据集小的时候手动多试几组参数比跑CrossValidator更快。我建议重点看三个参数的组合效果参数影响实践建议rank隐向量维度影响模型表达能力从8到20逐档尝试过高容易过拟合regParam正则化强度防止过拟合0.01到0.1之间网格搜索alpha隐式反馈置信度参数数据稀疏时适当调大到1.5左右参数调优时我强烈建议把每次实验的RMSE和参数组合记录下来。答辩时如果能拿出一张“不同rank下的RMSE对比表”说明你是真正做过实验的而不是只调了一版参数就交差。5.3 冷启动基于知识图谱的路径推荐协同过滤最大的短板是冷启动。新用户没有行为记录新酒店没有评分记录ALS基本无能为力。这个项目恰好有知识图谱可以从图谱路径出发做一个可解释的推荐。核心思路很简单找到用户已经表达过偏好的酒店集合沿着图谱关系扩展到共享标签的酒店按共享标签数排序取TopN作为冷启动推荐。用户偏好来源可以是用户评论过的酒店也可以是用户在可视化界面上主动选择的城市和标签。Cypher查询大致如下MATCH (u:User {user_id: U001})-[:REVIEWED]-(h:Hotel)-[:HAS_TAG]-(t:Tag) WITH u, COLLECT(DISTINCT t.name) AS favoriteTags MATCH (candidate:Hotel)-[:HAS_TAG]-(tag:Tag) WHERE tag.name IN favoriteTags AND NOT (u)-[:REVIEWED]-(candidate) RETURN candidate.name, COUNT(DISTINCT tag.name) AS commonTags, AVG(candidate.score) AS avgScore ORDER BY commonTags DESC, avgScore DESC LIMIT 10;这个查询在Neo4j里执行毫秒级出结果。为了让推荐更有区分度我还加了酒店的评分作为次级排序确保共享标签数相同的情况下优先推荐口碑好的酒店。这一方案之所以可解释是因为推荐的每个酒店都能给出一条“你和A酒店有共同兴趣A酒店和B酒店共享‘亲子友好’标签所以为你推荐B”的逻辑链。这个解释性在答辩时能直接讲给评委听也是纯协同过滤方案不具备的亮点。5.4 混合推荐策略与效果评估ALS适合有行为数据的老用户图谱路径推荐适合冷启动和新物品只靠任何一种都有盲区。我在实际实现里用加权融合把它们组合起来final_score 0.55 * als_score 0.30 * kg_score 0.15 * popularity_scoreALS得分已经是归一化后的预测评分图谱得分是归一化后的共享标签数热度得分是归一化后的点评数。权重根据数据集特性手动调整实测下来0.55、0.30、0.15这组权重在覆盖率和准确率之间比较平衡。效果评估除了RMSE还要算推荐列表的Precision10和召回率。具体做法是保留每个用户最近评论的酒店作为正样本看推荐列表Top10中有多少命中。我项目里的混合推荐Top10命中率约为21.6%比纯ALS的18.2%有明显提升冷启动用户场景下提升更明显。这个对比数据在论文和PPT里非常有用建议一定保留。再补充一个工程细节推荐结果要写回HDFS和MySQL不能每次前端请求都实时跑模型。离线计算每天跑一次生成新的推荐列表存入MySQL的rec_result表后端接口直接查表返回。实时推荐听着好听但对毕设来说离线加定时更新是最稳妥的架构。6. 可视化大屏让数据会讲业务的三个要点6.1 指标体系搭建可视化部分最容易犯的错误是“为了画图而画图”。我在设计大屏时先列了一组业务问题再倒推需要哪些图表哪些城市酒店供给多、均价高用中国地图热力图加城市TOP10柱状图用户偏好怎么变化用标签词云加评论关键词柱状图推荐效果如何展示用用户推荐列表加酒店详情抽屉酒店经营结构怎样用星级分布饼图加价格带与评分关系散点图知识图谱怎么体现用酒店关系图交互面板。大屏采用经典三栏布局中间是地图热力区域左边放城市排行和星级分布右边放价格带分析和推荐结果列表。顶部是总览指标卡显示酒店总量、评论总量、平均评分和推荐覆盖率。这种布局信息密度高、主次分明演示时一眼能看到核心结论。大屏配色要统一。我参考了几个数据可视化平台的配色案例用的是深蓝底加高亮青绿色方案。数据大屏的背景色用深色系比如#0f1c2e这类图表色板固定成5到6个颜色不要用ECharts默认的多彩色板否则大屏会显得杂乱。标题字体统一信息层级用字号和透明度区分所有指标卡需要带图标。这些细节在评委看演示时的加分效果非常明显。6.2 技术实现与联调要点后端我用Flask提供JSON接口前端用纯HTML加ECharts。ECharts的graph类型做关系图map类型做中国地图词云用echarts-wordcloud插件。接口设计成一次返回一整个图表的数据结构前端拉取后直接setOption避免过多交互逻辑。后端取数的核心逻辑不复杂大部分指标直接从MySQL的stats表查关系图数据从Neo4j查。一个容易忽视的点是接口响应要快。Neo4j如果开了Browser页面内存占用很高联调时出现过接口延迟。我的做法是把知识图谱探索页用到的关系数据查询结果缓存到MySQL前端浏览时直接读缓存既快又稳。大屏还有一个演示细节——轮询。我做了每隔30秒刷新一次接口的机制虽然底层数据是离线的但页面上可以看到刷新动态演示时更有“实时系统”的感觉。刷新时不要整页reload用ECharts的setOption增量更新就好否则会闪屏影响观感。from flask import Flask, jsonify import pymysql app Flask(__name__) db pymysql.connect(hostlocalhost, userroot, password123456, databasehotel, charsetutf8mb4) app.route(/api/stats/city_top) def city_top(): cursor db.cursor() cursor.execute(SELECT city, hotel_count, avg_price FROM stats_city ORDER BY hotel_count DESC LIMIT 10) rows cursor.fetchall() return jsonify({code: 0, data: [dict(zip([city, hotel_count, avg_price], row)) for row in rows]})7. 毕业设计交付源码、文档、演示全流程经验7.1 源码目录组织毕设评分不只是看系统能跑源码的组织结构也是重要考察点。我见过很多同学把爬虫、Spark、可视化代码全塞在一个文件夹里命名还叫test1.py、final_v2.py这种代码即使功能正常也会让评委扣分。我的推荐组织方式hotel-recommendation/ ├── crawler/ # Scrapy采集 │ ├── spiders/ │ ├── items.py │ ├── pipelines.py │ └── settings.py ├── etl/ # PySpark离线处理 │ ├── clean_hotel.py │ ├── feature_engineer.py │ ├── stats_analysis.py │ └── write_to_mysql.py ├── recommendation/ # 推荐模型 │ ├── als_train.py │ ├── kg_recommend.py │ ├── hybrid_recommend.py │ └── evaluate.py ├── graph/ # 知识图谱构建 │ ├── ontologies.py │ ├── import_to_neo4j.cypher │ └── query_cases.cypher ├── web/ # 可视化大屏 │ ├── app.py │ ├── templates/ │ └── static/ ├── docs/ # 项目说明、接口文档 └── README.md每个目录下配一个README写清楚做什么用、怎么运行。README不是给评委看的是给三个月后你自己看的。项目做到后期经常忘记当初某个脚本的作用简洁的注释能省大量回溯时间。7.2 LW文档写作框架LW文档毕业设计论文的写作框架我建议按这个顺序组织每个章节都有评分点需求分析部分把用户痛点、功能需求、非功能需求写清楚这是评委判断你对项目理解度的起点。系统设计部分重点画系统架构图技术栈、数据流、模块划分一目了然。数据库设计部分除了MySQL表结构还要画Neo4j图模型和关系说明这部分是差异化亮点。系统实现部分按采集、处理、图谱、推荐、可视化五个模块展开每个模块配合核心代码片段和运行截图。测试部分要至少包含功能测试、接口测试和推荐效果评估三块特别是推荐效果评估用数据说话比什么都强。文档最容易犯的毛病是“把实现代码翻译成文字”。正确的做法是讲设计思路和解决过程而不是把代码贴一遍。比如写爬虫章节时重点写“如何解决动态页面加载”“如何做增量断点续采”而不是罗列代码。7.3 答辩演示流程与高频问题答辩演示我建议卡在8到10分钟流程固定成先讲业务痛点和技术架构2分钟再演示爬虫采集日志和HDFS文件目录1分钟接着跑一条Spark SQL展示离线计算结果1分钟然后在Neo4j Browser里执行Cypher查询1分钟进入推荐效果演示随机选一个用户看推荐列表2分钟最后打开大屏做整体收尾2分钟。高频问题我提前准备过给大家参考为什么用Spark不用MapReduce答开发效率和迭代速度强调批处理场景下Spark SQL的生态能力。数据量多大为什么需要大数据架构诚实说明数据量不大但强调这是完整的大数据技术栈实践重点在链路和工程能力。推荐效果如何评估说出RMSE和Precision10的数值说明冷启动场景下图谱推荐比纯ALS提升多少。知识图谱为什么用Neo4j答多跳查询性能和Cypher表达能力。冷启动怎么解决这题必须自己主动提大部分评委都会追问提前准备好图谱路径推荐的示例能拿分。还有一个答辩细节如果评委让你现场改一个SQL或Cypher不要慌提前把几条核心查询模板背熟现场套数据改起来很快。真正被问倒的一般不是没答上来而是沉默太久不会救场。最后说点实际的。当初做这个项目时最让我头疼的不是某个算法而是把这么多组件串起来时不断出现的环境问题和数据问题。Hadoop和PySpark版本不兼容、Neo4j导入关系失败、ALS训练时报空指针每一个问题都要花不少时间排查。但整套系统真正跑通、大屏亮起来的那一刻你会觉得前面所有折腾都值了。如果你也在做类似的毕设记住一点先跑通最小闭环再逐步加亮点。数据链路通了后面的一切才有意义。