ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

大数据毕设实战:Hadoop+PySpark+Scrapy酒店推荐系统全解析

大数据毕设实战:Hadoop+PySpark+Scrapy酒店推荐系统全解析 又到了一年一度的毕业设计季节每年这个时候我都能在后台收到大量私信大数据方向的毕设到底做什么怎么做才能既有技术深度又能顺利通过答辩说实话我接触过很多做所谓“推荐系统”的同学一大半都是下载一个公开数据集跑一个现成的协同过滤代码再套个前端页面就交差了。数据本身不是自己获取的整个处理链路也走不通答辩的时候一问就露馅。这篇文章我就以自己实际带过的一个项目为例完整拆解一套真正“拿得出手”的大数据毕设——基于Hadoop、PySpark、Scrapy的酒店推荐系统同时包含酒店知识图谱构建、数据分析可视化和完整的Web交互。这套项目从零开始覆盖了数据采集、存储、计算、算法、可视化全链路也是我在实际环境中反复调试跑通的方案每一部分我都会讲清楚设计思路和踩坑记录希望能给正在选题或已经入坑的同学一个直接能参考的模板。从选题思路到系统落地这套酒店推荐系统到底做了什么1.1 一套完整的“数据闭环”是什么样的很多同学对大数据的理解停留在“用了Hadoop、Spark就算大数据”这是最大的误区。真正的难点在于把数据链路的每一个环节串起来形成一个能够自洽运转的闭环。这套酒店推荐系统的核心链路是Scrapy爬虫抓取真实的酒店数据 - 清洗后存入HDFS - PySpark做离线分析和特征计算 - 协同过滤算法生成个性化推荐 - 同时抽取实体关系构建知识图谱 - 最后在Web端完成推荐展示、图谱交互和可视化大屏。这样的设计有几个非常实际的好处。首先是数据来源真实可靠评审老师问“你的数据哪来的”你理直气壮回答“自己爬的”这就已经比一票用公开数据集的同学站得住脚。其次是技术栈覆盖全面从爬虫、大数据存储计算到机器学习算法、前端可视化每一层都有可讲的东西完全能撑起一篇毕业设计的深度。1.2 为什么选这四件套Hadoop、PySpark、Scrapy、知识图谱选型这件事不只是“听说这几个技术很火”而是要根据项目需求来。我做这套设计的时候选型逻辑是这样考虑的Hadoop担任的是底层存储和基础计算的角色。HDFS天然适合存储海量非结构化或半结构化的酒店数据比如评论文本、用户日志、爬取抓包返回的JSON而MapReduce虽然现在用得少了但作为离线批处理的底层思想写进论文里能体现你对分布式计算原理的理解。PySpark更像是把这些数据“盘活”的关键。用Spark做数据清洗和特征计算比单纯写MapReduce效率高太多而且Python接口对做毕设的同学极其友好。你在PySpark里写DataFrame操作感觉和pandas很像但底层是分布式执行这个“既熟悉又高级”的体验非常适合毕设场景。Scrapy是这套系统的数据入口。作为Python生态里最成熟的爬虫框架它的并发调度、中间件机制和Pipeline数据流设计可以非常优雅地支撑多页面、多字段的酒店数据采集。知识图谱是用来“拔高”的部分。单纯做推荐系统在毕业设计里已经不算新鲜了但如果你把酒店、城市、品牌、设施、用户偏好这些信息抽成实体和关系用图数据库Neo4j存储再在前端画出一张可交互的知识图谱整个项目的技术亮点立刻不一样了。这也是答辩时最容易引起老师兴趣的模块。1.3 毕设交付物包含哪些东西一个完整的毕设项目代码只是其中之一。这套项目正常交付时包含了全套源码、毕业论文LW文档、答辩PPT和一份详细的视频讲解。论文里我重点写了三章推荐算法设计与实现、知识图谱构建、大数据平台部署PPT则把系统架构和核心截图作为亮点展示详细讲解则是录制的系统演示覆盖了从爬虫启动到知识图谱交互的每个操作步骤。交付物完整的好处是无论导师从哪个维度验收你都有内容可讲。数据从哪来Scrapy爬虫系统设计细节2.1 目标站点分析与爬虫策略酒店数据去哪爬我当时选型的标准有三条一是数据维度丰富至少包含酒店名称、地址、价格、评分、评论数和经纬度二是页面结构相对规整便于批量解析三是数据量需求在几千条以上才有分析价值。国内主流的在线旅游平台基本都符合条件但不同平台的页面结构差异很大有的需要处理动态加载有的则藏在iframe里还有的需要登录才能看完整信息。这里要特别提醒一点爬虫不是写一次就完了更不是运行起来就一劳永逸。目标网站的前端结构经常改版包括标签属性变化、接口参数加密、反爬策略升级等这些东西都会直接导致爬虫失效。因此设计阶段就要把“易维护性”考虑进去字段解析和页面下载逻辑尽量解耦这样某一部分挂了修复成本会小很多。2.2 Scrapy的核心架构与代码实现Scrapy的工作流程可以简单理解为Spider发请求拿到响应通过Selector或Selenium解析出结构化字段打包成Item交给Pipeline做清洗和存储而下载中间件和爬虫中间件就像关卡一样夹在中间分别负责请求的预处理和后处理。我举一个Spider的核心代码片段这是抓取酒店列表页的最小实现import scrapy from hotel_spider.items import HotelItem class HotelListSpider(scrapy.Spider): name hotel_list start_urls [https://example.travel.com/hotels/city/beijing] def parse(self, response): # 定位酒店条目区域每个酒店一个card节点 hotel_cards response.css(div.hotel-card) for card in hotel_cards: item HotelItem() item[name] card.css(h3.hotel-name::text).get() item[price] card.css(span.price-num::text).get() item[score] card.css(span.score::text).get() item[comment_num] card.css(span.comment-num::text).get() yield item # 下一页链接用yield继续交给调度器 next_page response.css(a.next::attr(href)).get() if next_page: yield response.follow(next_page, callbackself.parse)这里有个容易被忽略的细节Item在不同版本中的用法略有差异。早期的Scrapy版本直接在Item里定义Field新版中你还可以用attrs库定义dataclass风格的Item类。两种写法都能运行但为了论文写起来更清楚我推荐使用dataclass风格因为字段类型一目了然from dataclasses import dataclass dataclass class HotelItem: name: str city: str address: str price: float score: float comment_num: int lat: float lng: float2.3 动态内容与iframe页面怎么处理很多旅游平台为了增加爬取难度详情数据都放到了动态请求里而列表页本身只是个壳。有些甚至把关键信息放在iframe里直接在Spider里用response.css根本取不到东西。这时候常规方案是引入Playwright或Selenium来渲染页面再把渲染完成的HTML交给Scrapy解析。scrapy-playwright是目前最顺手的方案它把Playwright的能力以中间件的形式整合进Scrapy生态使用起来非常自然。在settings.py里做以下配置DOWNLOAD_HANDLERS { http: scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler, https: scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler, } PLAYWRIGHT_LAUNCH_OPTIONS { headless: True, }然后在Spider中这样请求yield scrapy.Request( urldetail_url, callbackself.parse_detail, meta{playwright: True} )这样Scrapy就会用无头浏览器加载页面。遇到iframe嵌套内容时ProcessRequest可以在meta中增加playwright_page操作先用page.frame_locator定位到对应的frame再取内容。这个方案比裸用Selenium稳定得多内存控制也好很多。2.4 反爬应对与请求调度的平衡术说句实在话爬虫最大的坑不是解析页面而是反爬。目标站点常见的反爬措施包括IP频率限制、User-Agent检测、Cookie校验、验证码、JS加密参数等。毕设场景下不建议做那种对抗性极强的破解一方面法律和技术伦理上有风险另一方面也没有必要——我们需要的只是几千条能说明问题的数据控制频率、伪装请求头就足够。我在这个项目中的策略是用下载中间件维护一个UA池并叠加一个简单的IP代理池class RandomUserAgentMiddleware: def process_request(self, request, spider): ua random.choice(spider.settings.get(USER_AGENT_LIST)) request.headers[User-Agent] ua return None class ProxyMiddleware: def process_request(self, request, spider): proxy random.choice(spider.settings.get(PROXY_POOL)) request.meta[proxy] proxy return None除此之外最重要的是控制并发。Scrapy默认的并发是16但爬酒店这类站点我建议调到4到6下载延迟设置在1到2秒。这个节奏跑起来既不会触发封禁数据采集速度也不算慢。实际跑下来采集2000家酒店的详情加评论大概是两三个小时的事完全够用。2.5 清洗规则与字段落地数据爬到之后不能直接进HDFS一定要做清洗。我在Pipeline里做了三件核心的事一是字段校正。价格、评分这种字段从HTML里取出来是带、分这类符号的字符串必须统一格式化成float字符串里混有的空格、换行要strip掉经纬度如果缺失要根据酒店地址用离线地理编码补一次。二是去重。很多平台列表页会重复公示同一家酒店我在Pipeline里维护了一个seen集合以“酒店名城市地址”作为唯一键去重。这种方式比单个字段去重可靠得多。三是空值策略。核心字段如名称、城市如果为空直接丢弃该条记录而评论数为空时我可以填0因为后面做推荐时会用评分算权重评论数为0的代表冷门酒店本身也有分析价值。清洗完之后我按日期分区写JSON格式落盘到HDFS目录 /data/hotel/raw/20250301/ 下面方便后续Spark直接读取和溯源。离线计算的基座Hadoop平台搭建与PySpark数据处理3.1 Hadoop集群怎么搭最省心很多同学一提到搭建Hadoop就头皮发麻其实毕设场景根本没必要搭真实的分布式集群一台机器跑伪分布式模式就完全够了。所谓伪分布式就是在单机上同时运行NameNode、DataNode、ResourceManager和NodeManager等进程逻辑上和分布式一样只是所有进程都在一台机器里而已。毕设的侧重点是让你把原理讲清楚流程跑通伪分布式完全能达到这个目标而且省去了多台机器联调的痛苦。我实际使用的环境是一台8核16G内存的服务器CentOS 7系统装了Hadoop 3.3.x版本、Spark 3.3.x版本和对应的PySpark。启动前需要配置core-site.xml、hdfs-site.xml和yarn-site.xml这三个文件。core-site.xml里指定NameNode地址hdfs-site.xml里设置副本数为1yarn-site.xml配置ResourceManager地址。这里有一个新手必踩的坑很多教程会告诉你启动前先格式化NameNode步骤是bin/hdfs namenode -format但如果你操作不当、多次执行格式化会导致NameNode的clusterID和DataNode不一致结果数据节点死活起不来。我当年在这个问题上卡了一个下午。解决办法也不难先停掉所有进程把tmp目录下的dfs数据彻底删掉再重新格式化和启动。顺序一定是清空数据目录 - 格式化 - start-dfs.sh - start-yarn.sh。3.2 PySpark从HDFS读取数据做特征工程Hadoop启动之后HDFS就是整个系统的数据中枢。爬虫清洗好的数据写入HDFSPySpark再通过hdfs://协议的路径读取。这样做的好处是存储和计算分离符合大数据的经典范式论文里写出来也更专业。PySpark读JSON并做基础清洗的代码大致是from pyspark.sql import SparkSession from pyspark.sql.functions import col, when, isnan spark SparkSession.builder \ .appName(hotel_etl) \ .getOrCreate() df spark.read.json(/data/hotel/raw/20250301/*.json) df df.dropDuplicates([name, city, address]) df df.filter(col(name).isNotNull() (col(price) 0)) df df.withColumn( price_band, when(col(price) 300, 经济型) .when(col(price) 600, 舒适型) .when(col(price) 1000, 高档型) .otherwise(豪华型) )这里需要多说一嘴Spark的惰性求值机制。你在代码里写withColumn、filter这些操作时Spark并没有真正执行只有当遇到action操作比如.count()、.write时才会真正触发计算。理解这个机制很重要因为很多同学在写大数据代码的时候用pandas的思维逐行执行结果日志看不懂、性能也上不去。之后我把处理好的数据写回HDFS的清洗目录同时把用户评论数据单独抽出来为下一步的推荐算法和知识图谱构建做准备。3.3 用户评分矩阵怎么造推荐算法最经典的数据格式是“用户-物品-评分”三元组。但我爬到的数据并没有真实用户的下单评分因为那属于平台的核心数据我根本拿不到。这时候就要回到毕设的本质造数。我采用了“模拟用户行为”的方式为推荐算法生成输入数据先爬取几千条真实酒店数据包括评分、价格、评论数、地理位置、设施标签等然后根据这些内容物构造500个虚拟用户再按照用户的偏好类型给不同的酒店打分。比如模拟“预算敏感型”用户对经济型酒店打高分模拟“舒适优先型”用户对高档型酒店打高分评分范围1到5分。这样生成的评分矩阵虽然不是用户真实行为但具有明显的群体偏好差异跑推荐算法能出非常清晰的效果而且我在论文里会把数据构造方法完整说明不构成学术造假。3.4 PySpark在Windows和Linux下的版本兼容坑这几年很多同学的开发机是Windows这也是个大坑。PySpark在Windows下最常遇到的报错是Failed to locate the winutils binary in the Hadoop binaries这是因为Spark在Windows环境下需要winutils.exe和hadoop.dll来模拟Linux的Hadoop环境。你必须下载对应Hadoop版本的winutils放到一个目录然后配置HADOOP_HOME环境变量指向它。另外注意PySpark、Spark和Java版本之间必须匹配比如Spark 3.3.x要求Java 8或11Python 3.8以上版本不匹配会在启动时就抛出各类奇怪异常。我比较推荐的做法是本地Windows只做代码编写和小规模逻辑验证真正跑全量数据时把代码放到Linux服务器上执行既免去了winutils的折腾运行速度也快得多。推荐系统核心协同过滤算法实现与调优4.1 为什么选协同过滤而不是深度学习现在一提起推荐系统很多人的第一反应是深度学习。但毕设场景需要理性评估深度推荐模型如DeepFM、DIN需要大量样本和特征工程训练时间长、解释性差答辩时老师问起来你很难在十分钟内讲明白。而协同过滤作为推荐系统最经典的算法原理清晰、实现成本低、效果可解释非常适合作为毕业设计的主算法。协同过滤的核心假设是喜欢过相似物品的人未来也容易喜欢相似的东西。我在这套系统里同时实现了基于用户的协同过滤UserCF和基于物品的协同过滤ItemCF然后按权重融合取两者之长。4.2 基于用户的协同过滤实现UserCF的思路分三步第一步计算用户之间的相似度第二步找到和目标用户最相似的K个用户第三步根据这K个用户对某些酒店的评分加权预测目标用户对未评分酒店的评分。相似度我用的是余弦相似度计算方式是把两个用户的评分向量看作高维空间的两个向量用夹角余弦衡量方向的相似性。从PySpark处理的用户-酒店评分矩阵中我直接加载并转为Python字典然后用内存计算实现算法主体def user_cf_predict(user_id, k10): # 加载用户的评分字典 {user_id: {hotel_id: rating}} ratings load_rating_matrix() target_user_ratings ratings[user_id] # 计算目标用户与其他所有用户的余弦相似度 sims [] for other_id, other_ratings in ratings.items(): if other_id user_id: continue common set(target_user_ratings.keys()) set(other_ratings.keys()) if len(common) 0: continue # 点积 / 模长乘积 dot sum(target_user_ratings[h] * other_ratings[h] for h in common) norm1 sum(r ** 2 for r in target_user_ratings.values()) ** 0.5 norm2 sum(r ** 2 for r in other_ratings.values()) ** 0.5 sim dot / (norm1 * norm2 1e-9) sims.append((other_id, sim)) # 取相似度最高的k个用户 sims.sort(keylambda x: x[1], reverseTrue) top_k sims[:k] # 加权预测目标用户对候选酒店的评分 candidate_hotels set() for other_id, _ in top_k: candidate_hotels.update(ratings[other_id].keys()) candidate_hotels - set(target_user_ratings.keys()) pred_scores {} for hotel in candidate_hotels: score 0 sim_sum 0 for other_id, sim in top_k: if hotel in ratings[other_id]: score sim * ratings[other_id][hotel] sim_sum abs(sim) if sim_sum 0: pred_scores[hotel] score / sim_sum return sorted(pred_scores.items(), keylambda x: x[1], reverseTrue)[:10]基于物品的协同过滤主体思路类似只是把“用户相似”换成“物品相似”核心逻辑是先建立物品间相似度矩阵再根据用户历史评分过的物品去找相似的物品。4.3 混合推荐与热度兜底UserCF和ItemCF各有利弊。UserCF在用户数量少但物品数量多的时候效果更灵敏它更偏向社会化和热点发现而ItemCF更稳定适合用户兴趣比较固定的场景。我实际测试后发现单用任何一种都会出现部分用户推荐列表为空或冷门物品被遗忘的情况因此我做了加权混合最终得分 0.5 * UserCF得分 0.4 * ItemCF得分 0.1 * 酒店热度分。热度分是我额外设计的用于缓解冷启动问题。计算方式为热度分 0.4 * 归一化评论数 0.3 * 归一化评分 0.3 * 归一化点击浏览数。这个混合策略保证了系统对新注册用户无评分历史也能推荐当前热门酒店不至于页面空白。4.4 评估指标怎么写进论文毕设论文里光有算法还不够还得有实验分析。推荐系统最常见的离线评估指标有三个精确率、召回率和覆盖率。我的做法是随机留出20%的评分作为测试集用剩余80%训练算法然后对每个测试用户生成Top10推荐列表计算预测命中比例。实测下来这套混合推荐在500用户、3000酒店的数据规模下精确率大概在18%到22%之间召回率在12%左右覆盖率能到30%以上。这个数据不算惊艳但作为本科毕设已经很有说服力而且我把不同K值下的效果变化做了折线图表直接放进论文的实验章节。酒店知识图谱实体建模、Neo4j存储与前端交互5.1 从数据到知识图谱里有哪些实体和关系知识图谱本质上是把散落的、孤立的数据变成相互关联的知识网络非常契合酒店领域的业务形态。酒店数据天然适合用图结构表达因为酒店和城市、品牌、设施、用户之间天然存在大量关联关系。我把实体分为五类酒店Hotel核心实体属性包括名称、价格、评分、地址、经纬度城市City酒店所在城市品牌Brand酒店所属品牌如某国际连锁品牌或本土品牌设施Facility如健身房、游泳池、免费停车、行政酒廊等星级Star从经济型到豪华型的等级对应的关系有酒店-位于-城市、酒店-属于-品牌、酒店-提供-设施、酒店-属于-星级。有了这四类关系整张图就能表达出“上海的某连锁酒店提供健身房且属于高档型”这样的复杂信息。5.2 Neo4j写入与Cypher查询知识图谱的存储我用的是Neo4j它是目前使用率最高的图数据库。安装Neo4j之后我用PySpark处理好的结构化数据通过py2neo库批量写入。写一个最关键的Cypher例子用于创建酒店和城市的关系MERGE (c:City {name: 上海}) MERGE (h:Hotel {name: 上海某酒店, price: 780, score: 4.5}) MERGE (h)-[:LOCATED_IN]-(c)MERGE语句是Neo4j中非常重要的概念如果节点已存在则不重复创建这是防止批量导入大量重复数据的关键。我还为酒店和品牌、酒店和设施创建了类似的关系。等数据全部导入后我可以执行这样一条查询找出上海所有评分大于4.5且带健身房的酒店MATCH (h:Hotel)-[:LOCATED_IN]-(c:City {name: 上海}), (h)-[:HAS_FACILITY]-(f:Facility {name: 健身房}) WHERE h.score 4.5 RETURN h.name, h.price, h.score这种多跳关联查询在传统关系型数据库里要写多重JOIN而在图数据库里就是一行模式匹配的事。把这个查询示例写进论文能非常直观地体现知识图谱的查询优势。5.3 图谱可视化从Neo4j到Vue前端Neo4j自带的Browser就可以可视化图结构但既然是个Web毕设项目就必须在自家系统里嵌入一个交互式的知识图谱页面。技术选型上我用的是Vue3加ECharts的关系图。ECharts虽然常用图表类型是折线图和柱状图但它的graph系列也能很好地展示关系网络。在Vue3中我通过Axios调用后端接口把Neo4j查到的节点和关系转成ECharts需要的nodes和links格式const chartData { nodes: res.data.nodes.map(n ({ id: n.id, name: n.name, category: n.category, symbolSize: n.category Hotel ? 60 : 40, itemStyle: { color: categoryColor[n.category] }, })), links: res.data.links.map(l ({ source: l.source, target: l.target, label: { show: true, formatter: l.relation } })) };ECharts关系图支持节点拖拽、缩放、点击高亮等交互。这个页面做完之后用户可以点击某个城市节点图谱会自动高亮该城市下的所有酒店及其关联设施交互体验非常直观。在答辩演示现场这个页面往往是老师停留时间最长的一块。数据分析可视化与Web系统整体集成6.1 从统计报表到多维度可视化数据分析可视化是体现数据处理能力的重要窗口也是毕设中比较容易出效果的部分。我基于HDFS中存储并经过Spark聚合的结果数据用图表展示了多个维度的分析内容城市酒店数量分布用柱状图展示热门旅游城市的酒店供给量排名价格区间分布用饼图展示经济型、舒适型、高档型、豪华型的占比评分与评论数散点图用散点图观察酒店评分和评论热度之间的相关性设施词频统计用词云展示出现频率最高的酒店设施关键词这些图表全部由ECharts渲染数据由后端的SpringBoot或Flask接口提供。Spark预聚合的数据写入MySQL或HBase接口再从其中查询返回给前端整体链路清晰且每一层都有事可讲。别小看这几个图表答辩时老师很喜欢问“你分析了哪些维度”“发现了什么规律”提前准备一两个分析结论很有必要。比如我当时总结出热门旅游城市的中档酒店数量最多但评分和评论数的相关性并不显著说明口碑好的酒店不一定是热门酒店。6.2 系统后端架构与前端页面后端我采用了SpringBoot原因是Java生态成熟、和Hadoop栈关系紧密而且很多同学的课程项目用的就是Java。为了让前端调用方便我把推荐接口、知识图谱接口和统计分析接口放在一起统一管理。整个Web应用包含四个核心页面推荐页用户输入ID或选择偏好系统返回Top10推荐酒店列表知识图谱页展示酒店关联关系网络支持节点点击和缩放拖拽数据大屏页用图表聚合展示全国酒店数据分析结果爬虫监控页展示爬虫运行状态、最近采集条数等前端用Vue3加Element Plus搭界面样式追求干净直接、偏“数据产品”风格。这个系统做完以后无论是截图放论文还是现场演示效果都远超过那种只有一个命令行输出结果的毕设。6.3 系统集成时最容易出的问题集成环节最常遇到的就是环境变量和端口冲突。Hadoop的50070端口老版本是50070新版本是9870、Yarn的8088端口、Spark的4040端口、SpringBoot的8080端口、Neo4j的7474端口、Vue DevServer的5173端口这些默认端口之间并不冲突但如果你本机跑着其他服务很容易被占用。我当时就遇到过8080被占用导致SpringBoot启动失败的情况最后把所有服务的端口都列成一个表格逐个检查才解决。另外一个常见问题是跨域。Vue开发服务器默认跑在5173端口后端跑在8080端口前端直接请求后端接口会报跨域错误。解决办法是在后端配置CORS过滤器允许所有来源的跨域请求。这个属于小坑但频率极高提前配置好能省去大量联调时间。实战排雷从Hadoop到Spark再到爬虫的典型问题汇总7.1 Hadoop相关格式化失败与DataNode连不上Hadoop启动格式化是高频问题。网上教程说启动前先格式化但没说不能反复格式化。如果你格式化一次启动了又停了改完配置又格式化就会导致NameNode的clusterID与DataNode不一致。现象是jps命令能看到DataNode进程但Web界面里DataNode列表是空的。解决办法是把Hadoop的tmp目录和dfs的name/data目录全部删除重新格式化。另外我建了个习惯每次做重大配置修改之前先备份原配置文件避免改坏了找不到原始状态。7.2 PySpark相关内存溢出与Shuffle异常PySpark跑全量数据的时候我遇到过OutOfMemory错误。原因是在做用户相似度矩阵时我试图把全量用户的相似度广播到所有Executor数据量一大内存直接爆掉。解决办法第一是调大Executor内存在提交任务时设置spark.executor.memory4g第二是优化代码把需要广播的数据控制在最小范围只广播TopN相似用户而不是全量矩阵。这个教训很适合写在论文的“系统优化”部分能显示出你对分布式计算的深入理解。7.3 爬虫相关验证码、字段缺失与封禁我实际爬取过程中印象最深的是验证码问题。刚开始爬某个平台正常浏览页面完全没有验证码但只要爬虫速度一快立刻弹验证码。后来我把下载延迟调到2秒并启用IP轮换基本就能稳定绕过。字段缺失也很常见部分民宿类酒店没有评分这部分数据我做了特殊标记在推荐算法里用默认评分兜底处理。整个项目从爬虫到可视化大概花了三周左右的时间其中环境搭建和版本兼容问题占了将近一半。这也给正在做毕设的同学一个建议不要把时间排得太满给环境问题留足缓冲期。技术上遇到的小问题绝大多数都能在官方文档或者Stack Overflow找到答案关键是学会看日志Java和Python的报错堆栈信息其实已经把问题定位得很清楚了静下心来自查往往比盲目搜索更快。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表