ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hadoop+Spark+Hive酒店推荐系统毕设实战:从爬虫到可视化全流程

Hadoop+Spark+Hive酒店推荐系统毕设实战:从爬虫到可视化全流程 计算机毕业设计选了“HadoopSparkHive酒店推荐系统”听起来就是一个典型的“大数据全家桶”组合。很多同学看到这个题目第一反应是终于有个能写进简历的大数据项目了但真拿到手又有点慌——爬虫怎么写、数据怎么存、推荐怎么算、最后怎么演示每个环节都是一道坎。我当时带人做这个题的时候走完一遍才发现它根本不是“写个推荐算法”那么简单而是一条从数据采集到数据仓库、再到离线计算和可视化的完整流水线。这篇内容就是围绕这个题目把整条链路拆开揉碎了讲清楚。不管你是刚拿到这个毕设题目、准备自己从零写还是已经在调试网上下载的源码包这篇都能帮你把每一块逻辑顺明白知道每一步在做什么、为什么这么做、踩坑点在哪。1. 这个毕设题目为什么值得做——先把项目定位搞清楚1.1 题目拆解这不是一个“推荐系统”而是一条完整的数据流水线先别急着写代码。拿到这个题目第一件事是搞清楚它到底考你什么。从标题上看它把“HadoopSparkHive”直接放在项目名前缀说明出题方或答辩老师想看到的是“你有能力把分布式的存储和计算框架真正用起来”而不只是写一个能跑出推荐结果的Python脚本。拆开看它其实包含四大模块数据采集层酒店爬虫把公开网页上的酒店名称、价格、评分、评论、位置等信息抓下来。数据存储层Hadoop HDFS做分布式存储Hive建数仓表把原始数据变成能分析的结构化数据。计算与推荐层Spark从Hive里读数据做特征处理跑协同过滤推荐算法产出“猜你喜欢”的酒店列表。可视化与业务层把统计结果和推荐结果通过Web页面展示出来形成“能看”的酒店数据可视化大屏和推荐页面。说白了这就是一个涵盖了大数据全链路的小型离线数仓项目。它不是那种单机跑个Python脚本就结束的课设而是每一步都在“大数据”的语境下工作。这个定位想明白了你就知道后面每个模块的选型和实现方式都该往哪个方向走。1.2 技术选型为什么是这“三大件”而不是别的很多同学会问做个酒店推荐我用Python加Pandas加一个Flask页面不香吗答案当然是香但那是“数据分析作业”不是“大数据毕业设计”。毕设题目里点名Hadoop、Spark、Hive就意味着你必须在分布式环境下完成数据存储和计算这套选型本身就是题目要考核的重点。具体拆一拆这三者的分工Hadoop负责底层存储HDFS和资源调度YARN。你爬到的几百兆或几个GB的原始数据、清洗后的数据都放在HDFS上而不是本地磁盘。Hive把HDFS上的结构化文件映射成二维表用类SQL语法HiveQL做数据清洗、过滤、统计。它解决了“分布式文件系统上怎么查数据”的问题——你不用写MapReduce写SQL就行。Spark负责更重的计算任务比如跑ALS协同过滤推荐算法。Hive做ETL和简单统计没问题但跑机器学习类的迭代计算还是Spark的强项。Spark可以从Hive表里直接读数据和Hive是无缝衔接的。从任务分工也能看出来这不是“资源重复”而是“各干各的专业活”。我见过不少同学把题目改成“简单版”爬虫爬完数据存MySQLPython算推荐ECharts做可视化。这样也能交但答辩时老师问“你的Hadoop呢”你只能回答“没用到”这分数就不好看了。提示如果你的机器配置一般可以合理选择“Hadoop伪分布式Hive本地模式Spark Local模式”的组合。只要代码逻辑是分布式思路展示效果不差反稳。后面我会专门讲环境怎么取舍。2. 数据从哪来酒店爬虫模块实战拆解2.1 爬虫目标与合规边界爬虫是整个项目的数据源头也是很多同学觉得“最没技术含量”但实际坑最多的环节。先明确爬什么以酒店信息为例通常需要四个维度的字段基础信息酒店名称、地址、商圈、星级、经纬度价格信息最低价、房型价格区间评价信息评分、评论数、好评率特色标签“近地铁”、“含早餐”、“免费取消”等这些信息一般分布在OTA在线旅游平台网站的列表页和详情页。爬虫的设计上优先抓列表页详情页可以用异步接口或详情页二次请求来补数据。这里必须提醒一句爬虫模块要严格注意合规问题。毕业设计属于学习研究用途爬取时可以抓公开可见的数据但要尽量降低请求频率、控制并发不要对目标站点造成压力更不能把数据用于任何商业用途。我建议在代码里显式设置请求间隔比如每抓一页sleep 1到3秒并设置合理的User-Agent和Referer。这既是保护自己也是让数据采集过程更接近真实的工程实践——一味模仿搜索引擎的抓法很容易被限制。2.2 爬虫实现的核心步骤以Python为例爬虫从零实现分四步分析目标页面结构确定列表页URL规律。用requests库请求页面拿到HTML内容处理编码和Cookie。用BeautifulSoup或XPath解析HTML提取字段。清洗字段后存成结构化文件CSV或JSON供后续上传HDFS。import requests from bs4 import BeautifulSoup import time import csv headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } def fetch_hotel_list(page): url fhttps://example-hotel-site.com/list?citybeijingpage{page} resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) hotels [] for item in soup.select(.hotel-item): name item.select_one(.name).text.strip() price item.select_one(.price).text.replace(¥, ).strip() score item.select_one(.score).text.strip() hotels.append([name, price, score]) return hotels with open(hotels.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([name, price, score]) for page in range(1, 20): rows fetch_hotel_list(page) writer.writerows(rows) print(fpage {page} ok, got {len(rows)} items) time.sleep(2)这里有一个很关键的点如果只爬静态HTML用requests加BeautifulSoup就够了不需要上Selenium因为Selenium会启动浏览器爬1000条数据要等很久。但有些站点数据是异步加载的HTML里只有空壳这时可以在浏览器开发者工具里找到真实的XHR接口直接请求那个JSON接口解析速度反而快得多也稳定得多。2.3 清洗与落地的几个关键点爬下来的数据是脏的不能直接进数仓。常见问题有价格字段里有“¥”、逗号、“起”等多余字符要去掉后转成浮点数。评分字段可能是“4.5分/1032人评价”要把分数和评论数拆开。缺失值有些酒店没有星级或标签需要考虑默认值填充或删行。重复值同一个酒店在列表页可能出现多次应该按酒店名称或平台ID去重。清洗逻辑可以用Pandas写一次性的预处理脚本也可以把清洗逻辑放到Hive的SQL里做。我的建议是先做一层简单清洗保证数据格式能解析剩下的字段规整放到Hive ETL阶段这样你答辩时可以说“清洗采用了前置脚本数仓ETL双层方案”这比单层方案听起来完整得多。3. 数据仓库与离线批处理Hive Hadoop 在这里到底干什么活3.1 数据落地到HDFS为什么不用MySQL直接存爬虫产出的是CSV或JSON文件接下来就是把它送进数据仓库。这里很多同学会犯一个“惯性错误”直接把数据分批INSERT到MySQL然后Hive表用JDBC映射MySQL。这样当然能跑通但绕了一大圈Hadoop就变成了纯摆设答辩时一问就露馅。正确的做法是把清洗后的文件先PUT到HDFS的指定目录然后用Hive建一个外部表指向这个目录。这不是为了炫技而是因为HDFS本身就是为海量文件存储设计的。爬虫单机跑了1000条数据你可能觉得MySQL也没问题但毕设的“种子数据”完全可以多爬几万条模拟真实场景。HDFS的分布式存储和副本机制就是为了解决这个问题存在的。上传HDFS的命令非常简单hdfs dfs -mkdir -p /user/hotel/raw/hotel_info hdfs dfs -put /root/hotels.csv /user/hotel/raw/hotel_info/不过在实际操作中我发现一个更好的做法是写一个Shell脚本或Python脚本自动完成“清洗-上传HDFS-执行HiveSQL建表”。手动一条命令一条命令敲第一次可以复现和写文档时会特别痛苦。自动化之后整个流程的复现成本就低很多也体现了工程能力。3.2 Hive建表与ETL表结构设计、分区、数据清洗数据进了HDFS接下来就是Hive出场。先设计原始表再用SQL清洗生成业务表这是数仓分层的标准思路。-- 原始数据表直接映射CSV文件 CREATE EXTERNAL TABLE IF NOT EXISTS hotel_raw( name STRING, price STRING, score STRING, comment_num STRING, address STRING, star STRING, tags STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY , STORED AS TEXTFILE LOCATION /user/hotel/raw/hotel_info; -- 清洗后的业务宽表 CREATE TABLE hotel_clean AS SELECT name, CAST(REPLACE(price, ,, ) AS DOUBLE) AS price, CAST(split(score, 分)[0] AS DOUBLE) AS score, CAST(REPLACE(comment_num, 条评价, ) AS INT) AS comment_cnt, address, CASE WHEN star THEN 未知 ELSE star END AS star, tags FROM hotel_raw WHERE name IS NOT NULL AND price IS NOT NULL;这里有三个细节值得注意第一为什么外部表用STORED AS TEXTFILE因为原始CSV就是文本格式Hive直接读最省事。到了清洗后的表如果想追求查询性能可以改成STORED AS ORC加压缩跑起来快很多。第二为什么要做“原始表”和“清洗表”两层因为数仓的核心思想就是“原始数据不动通过ETL产出分析层”。这在简历和毕设文档里都是标准写法也方便回溯问题。第三实际用Hive做主键去重时ROW_NUMBER()窗口函数是最好用的CREATE TABLE hotel_dedup AS SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY name ORDER BY score DESC) AS rn FROM hotel_clean ) t WHERE rn 1;这样按酒店名称分组每个酒店只保留一条评分最高的记录。比group by后再join老半天高效得多。3.3 Hadoop/YARN资源调度的坑小文件、内存、堆内存这个模块里我见过最多的问题不是SQL写不出来而是环境本身各种爆雷。说几个高频症状“Job has failed”或者“Container killed by YARN for exceeding memory limits”多半是YARN分配的内存不够。伪分布式模式下MapReduce任务默认1GB内存但虚拟机只给了2GB很容易挂。解决方法是调小每个容器的内存。文件数量特别多、任务参数反复拉满爬虫如果按天存储一个文件夹几千个小文件Hive跑起来元数据压力很大。建议在清洗后用INSERT OVERWRITE合并一次或者在上传前用pandas合并分片。SSH和端口问题Hadoop启动后如果DataNode和NameNode起不来先检查hostname映射、免密钥登录、防火端口。我见过很多同学卡在“start-dfs.sh”之后jps一看少了个进程结果只是没配置HADOOP_HOME的环境变量。经验之谈如果你是在自己的笔记本上跑不要死磕“完全分布式三台虚拟机”。伪分布式模式跑通全流程性价比最高。但伪分布式不代表你代码上可以“单机化”该用HDFS Path的地方不要写本地路径该用SparkSession读Hive表的地方不要用Pandas read_csv。4. 推荐模块Spark 协同比矩阵分解更现实4.1 推荐到底用什么算法从协同过滤到ALS酒店推荐系统的核心算法大多数毕设都会选“协同过滤”这几乎是这一类题目的标准答案。原理很直白通过用户的历史行为比如浏览、收藏、预订酒店找到和他口味相似的其他用户或者找到他喜欢酒店的相似酒店然后把相关酒店推荐给他。在Spark MLlib里落地最方便的是基于ALS交替最小二乘法的矩阵分解协同过滤。ALS会把“用户-酒店评分矩阵”拆解成两个低维矩阵分别用隐因子表示用户偏好和酒店特征。它不需要你做特别复杂的特征工程只需要一份“用户ID-酒店ID-评分”三元组就能训练出可用的推荐模型。为什么选ALS而不是基于物品的协同过滤或基于内容的推荐原因有两个ALS在Spark里有现成实现可以直接调库代码量小效果可控。矩阵分解是推荐系统中的经典模型算法原理答辩时好讲清楚不像深度学习模型那样容易被老师追问到答不上来。当然如果老师问“为什么不用深度学习”你可以答离线推荐场景下ALS在数据量适中时表现不弱于深度模型而且工程实现简单、可解释性强。这在工程落地时是实打实的优势。4.2 Spark ALS 的实操参数细节用Spark跑ALS核心代码通常长这样from pyspark.sql import SparkSession from pyspark.ml.recommendation import ALS from pyspark.ml.evaluation import RegressionEvaluator spark SparkSession.builder \ .appName(HotelALS) \ .enableHiveSupport() \ .getOrCreate() # 从Hive读用户行为表 ratings spark.sql(SELECT user_id, hotel_id, rating FROM user_hotel_rating) train, test ratings.randomSplit([0.8, 0.2], seed42) als ALS( userColuser_id, itemColhotel_id, ratingColrating, coldStartStrategydrop, rank10, maxIter10, regParam0.1 ) model als.fit(train) predictions model.transform(test) evaluator RegressionEvaluator( metricNamermse, labelColrating, predictionColprediction ) rmse evaluator.evaluate(predictions) print(fRMSE {rmse})这里有几个参数需要认真调rank隐因子个数。rank太小模型表达能力弱rank太大训练慢且容易过拟合。冷启动场景下从8到12起步比较稳。maxIter迭代次数一般10到20次就够了再多收益很小。regParam正则化参数用来防止过拟合一般从0.01到0.1之间网格搜索。我还想特别说下coldStartStrategydrop这个参数它表示遇到冷启动用户或物品时直接丢弃预测结果而不是给个NaN。这是Spark ALS的一个经典坑默认策略会产生很多NaN预测导致评估指标全是NaN看起来像模型坏了其实只是参数没设。4.3 冷启动问题怎么处理协同过滤面前有一个绕不开的问题新用户没有行为数据新酒店没有评分记录算法推荐不出来。这个在答辩时几乎必问。我的处理思路是加一层“兜底策略”。当用户没有历史行为、ALS无法给出个性化推荐时就回退到热门酒店推荐按评分人数降序排序取前N个或者按城市、价格区间做条件筛选。这样接口永远有数据返回用户在页面上也始终能看到推荐内容。具体实现上可以在Spark里先算一个热门表SELECT hotel_id, COUNT(*) AS cnt, AVG(score) AS avg_score FROM user_hotel_rating GROUP BY hotel_id ORDER BY cnt DESC LIMIT 20;然后代码里判断ALS模型的输出结果若为空就查这张热门表。这种规则与算法结合的方案在真实工业界也很常见。5. 可视化与系统整合给答辩老师看到的东西5.1 可视化框架选择ECharts 最快出效果可视化是毕设的“门面”。老师第一眼不看你的HDFS文件目录也不看你Spark日志而是先看你的展示页面效果。酒店可视化页面常见的图表有这么几类全国/某城市酒店数量Top10柱状图酒店价格区间分布饼图酒店评分分布直方图热门酒店推荐列表酒店地址分布的中国地图/省市级地图热力图用户推荐结果展示卡片框架方面ECharts是我最推荐的。它上手快示例库丰富能直接套模板改数据不需要写复杂的前端逻辑。如果你不想手撸原生JavaScript可以直接用Vue或Uniapp配合ECharts组件也可以直接写一个HTML页面引入ECharts CDN。5.2 前后端接口与展示逻辑可视化数据从哪来两种常见做法在Hive里跑统计SQL把结果导出成JSON或CSV前端直接读静态文件展示。写一个后端服务Spring Boot、Flask或FastAPI后台用Spark/Hive跑任务结果存到MySQL或缓存中前端通过接口动态请求。第一种做法简单适合快速出效果第二种做法更接近真实项目适合写进“系统设计”章节。我个人建议统计图表可以用静态JSON但“推荐结果”这个功能必须做成动态接口因为推荐结果和当前登录用户有关不能写死。接口设计上后端至少暴露这几个API/api/hotel/stats返回价格分布、评分分布、城市Top等统计数据/api/recommend?userIdxx返回ALS模型训练出的推荐酒店列表/api/hot返回热门酒店列表前端页面用Ajax或Axios调接口拿到数据后setOption渲染ECharts图表。这个链路一旦跑通整页就能“活”起来。5.3 打包部署与答辩演示要点毕设答辩演示时最大的风险是现场启动不了环境。我见过太多同学在答辩前五分钟还在敲start-dfs.sh结果集群起不来。这里有几个非常实用的建议提前把环境启动好用快照或恢复脚本避免演示现场临时配置。准备一套“演示数据”和“演示模型”不用现场重新训练。可以提前把ALS模型保存到磁盘展示时直接加载秒出推荐结果比现场跑10分钟训练强太多了。页面优先展示“效果图”类内容。先放可视化大屏让老师有个直观印象再逐步讲到Hive SQL、Spark代码、爬虫脚本这样讲解节奏是“从宏观到微观”比反着讲效果好。还有一个容易被忽视的细节演示时不要暴露本地绝对路径和数据量过小的“破绽”。比如你爬虫只爬到50条数据老师问“怎么证明你这个系统能处理大数据量”你不一定非要现场生成100万条数据但可以说清楚你的架构设计支持横向扩展并给出在测试环境下处理的数据量对比结果。6. 常见问题与踩坑实录6.1 环境安装与启动阶段这段是毕设路上最大的坑区。我把它整理成一个速查表遇到问题直接对照排查现象可能原因解决思路jps启动后缺少DataNodehdfs格式化时有残留删除tmp目录重新bin/hdfs namenode -formatHive访问MySQL元数据库报错MySQL connector版本不匹配确认mysql-connector-java版本和数据库版本一致Spark读Hive表报“Table not found”没启用Hive支持或元数据没同步启动时加enableHiveSupport()检查hive-site.xml是否正确分发YARN任务反复失败报内存超限YARN分配内存和VM内存不匹配调小yarn.scheduler.maximum-allocation-mb和mapreduce容器内存Hadoop端口被占用上次退出没关闭进程用netstat查端口kill残留Java进程NameNode进入Safe Mode异常关闭HDFS数据块状态不对用hdfs dfsadmin -safemode leave临时退出排查磁盘空间特别提醒一下Hadoop的hostname和hosts配置我建议从一开始就固定下来。很多同学安装时没有统一规划导致后面Spark和Hive拿到的主机名不一致各种连接不到。第一次安装时多花十分钟做配置检查后面能省下几小时的排错时间。6.2 数据与业务逻辑的坑爬虫爬下来的数据经常出现“看起来能入库、算起来就崩”的情况这里有几个稳定的坑价格字符转数字报错“价格像283元起”直接CAST必然会失败应该先用正则提取数字部分。评分字段存在缺失ALS训练数据要求rating列不能为null建议建模前过滤掉空值或用平均值填充。用户ID重复如果爬到的用户行为数据里有大量重复浏览记录ALS对重复样本的权重会异常训练时间成倍增加推荐效果也会偏差。可以先按(user_id, hotel_id)做一次汇总取平均分。数据倾斜如果某个城市的酒店数量特别多Spark处理时可能出现一个Executor撑死、其他Executor饿死的情况。可以加盐重分区或调整并行度来缓解。实操心得我建议在你把爬虫和Hive打通后先做一次“全链路冒烟测试”就是从爬虫到可视化只用一个很小数据集跑通确认每个环节的输入输出格式都对再批量爬完整数据。这个“先跑通再跑量”的习惯能救很多人的命。6.3 推荐结果质量差怎么办很多同学训练完模型发现推荐出来的酒店跟用户实际兴趣关系不大。这时先别怀疑算法选错了按顺序排查评分数据稀疏度是否过高如果90%的用户只对1家酒店有过评分矩阵分解很难学到有效特征。这里可以适当增加用户行为数据或把“收藏”“浏览”这些行为也转成隐式评分。归一化是否做对评分数值范围如果是0到100ALS默认会把输出压到某个区间导致评估指标和展示结果很奇怪。建议评分统一为1到5分制。有没有做数据过滤如果大量酒店没人评过分ALS对它们的隐因子向量基本是随机初始化推荐出来自然乱。可以先过滤掉交互数过少的酒店。真要在答辩前快速改进展示效果还有一个万金油技巧对ALS推荐结果做“规则后处理”比如保底过滤掉评分低于3.5分的酒店再按价格区间排序。这样推荐列表看起来会更“合理”也更容易讲故事。7. 这次做毕设我学到的三件事如果你也是今年做这个题目或者已经在折腾大数据的路上我想说几句掏心窝的话。第一别被“全家桶”吓到。Hadoop、Spark、Hive听起来门槛高但它们的核心思想都是“把大问题拆成小问题分给多台机器一起算”。你只要先跑通一个最小版本再慢慢往里面加模块这个项目并没有想象中那么可怕。第二毕设项目最重要的是“完整闭环”。哪怕你的数据量不大算法很简单只要把“爬虫—存储—计算—推荐—可视化”这条链路完整跑通每一步的输入输出都交代清楚这就是一个合格的大数据毕设。反过来如果你只写了5000行算法代码但没有数据支撑老师反而会觉得不落地。第三也是最容易被低估的一点一定要把“踩坑记录”留好。你在Hadoop配置、Hive SQL、Spark调参过程中遇到的所有问题不仅是你答辩时的谈资也是你未来面试时的素材。网上那些写得好的大数据博客几乎没有一篇不是从真实的坑里爬出来的。你这次踩的每一个坑都可以变成下一份Offer的垫脚石。
返回列表