这是一个信息严重不对称的时代兼职信息更是重灾区。58同城、BOSS直聘、智联上面岗位真假难辨又散落在各个APP里学生党想在课余找个靠谱兼职基本靠运气。我当时做这个课题的时候就一直在想能不能做一个平台把散落各处的兼职信息统一抓取下来清洗干净然后用Hadoop这类大数据组件做统计分析再让AI大模型根据每个用户的能力和偏好来做个性化推荐——把“人找信息”变成“信息找人”。这个项目的完整名字就是基于SpringBoot大数据爬虫Hadoop智能AI大模型的兼职聚合与个性化推荐平台本质上是一条从数据采集、存储计算到智能推荐的完整技术链路。这篇文章我打算把我当时完整的设计思路、技术选型、踩坑过程和答辩经验全部写出来适合正在做毕业设计或者想从零了解一个爬虫采集数据 大数据离线处理 LLM推荐全流程项目的同学参考。整个系统不算太复杂但技术栈跨度很大从Python爬虫到HDFS再到SpringBoot接口和前端大屏每一层都有不少细节坑。我把这些坑都记下来了下面挨个拆开讲。1. 项目整体设计与思路拆解1.1 为什么选择这套四层架构这个项目的题眼在于聚合和个性化推荐两个词。聚合意味着你需要一个能持续抓取多来源信息的数据采集层个性化推荐意味着你光把数据存起来不够还要做用户画像、做匹配计算。所以架构上我直接拆成了四层数据采集层、数据存储与计算层、业务服务层、智能推荐层。数据采集层用Python写爬虫requests Scrapy Selenium混合抓取公开的兼职招聘信息包括岗位名称、薪资、公司、学历要求、工作地点、发布时间、岗位标签等半结构化字段。数据存储与计算层原始数据清洗后写入HDFS用Hive做离线统计分析比如哪个城市兼职岗位最多什么时段发布量最大同时把清洗后的业务数据同步到MySQL供Web平台查询。业务服务层SpringBoot负责用户注册登录、兼职信息CRUD、收藏投递、浏览记录等常规业务提供RESTful接口。智能推荐层先做基于用户的协同过滤和基于物品的协同过滤做候选召回再用AI大模型对岗位描述和用户简历标签做语义匹配打分得到最终的TopN推荐列表。有个问题很多人会问为什么非要上Hadoop如果只是做毕设或课设数据量几千条MySQL直接都能跑。我当时的想法是这个项目的题目既然叫大数据爬虫那就必须体现大数据组件的存在价值。HDFS在这里承担的是数据源归档的职责爬虫抓下来的原始JSON、去重后的清洗数据、Hive分析结果都沉淀在里面MySQL只保留在线查询需要的那部分业务数据。这种离线大数据 在线小数据的分工在实际企业的离线数仓架构里是非常常见的思路写进论文里也站得住脚。另外还有个细节搜索热词里经常提到springboot整合flink。我当时并没有引入Flink原因很简单兼职信息不是实时性要求非常高的业务爬虫每小时跑一次就足够了用Flink做实时流计算属于杀鸡用牛刀。但在论文的展望部分我会专门写一段如果未来需要实时抓取和秒级更新可以引入Flink或Spark Streaming——这样一个点既能体现你对实时计算有认知又不会给自己增加巨大工作量。技术选型最忌讳的就是什么都往上堆导致每个环节都做不透。1.2 项目模块划分与核心实体设计整个系统在代码层面分成这么几个Maven模块我用的SpringBoot 2.7和Hadoop 3.3.x的兼容性相对最好SpringBoot 3.x会引入Jakarta命名空间很多老教程会踩坑bootstrap启动模块放启动类和全局配置类。common公共模块统一返回体Result、异常处理、JWT工具类、常量类。system用户管理模块注册、登录、权限拦截、管理员后台。job兼职信息模块岗位CRUD、搜索筛选、收藏、投递记录。analysis数据分析模块对接MySQL里的统计结果以及从Hive产出的宽表给前端数据大屏提供接口。recommend推荐模块协同过滤候选集计算 大模型打分服务。数据库设计上核心表我给了这些user用户表、user_resume用户简历标签表用逗号分隔存标签简单实用、job_position岗位表、job_collect收藏表、job_apply投递表、user_behavior行为日志表、recommend_record推荐记录表。这里有个关键的冗余设计理念大数据分析结果落地时为了查询性能特意做了由Hive产出统计宽表再由定时任务同步到MySQL的设计。比如城市岗位热度表 city_job_stats由Hive离线分组聚合出结果再用Sqoop或者直接用JDBC定时同步。Web端查大屏数据时只需要select这几张表毫秒级返回。在写系统总体设计这一章论文的时候我建议一定要画好两张图技术架构分层图爬虫层 → 数据层 → 服务层 → 应用层和功能模块图。答辩时老师特别喜欢问的第一句话就是你这系统整体流程是怎么走的你如果能一分钟把这两张图讲清楚基本就能稳定住全场的节奏。2. 数据采集环节的设计与反爬反抓实战2.1 爬虫技术栈选型与采集流程数据采集是整个平台的数据源头不能出问题。我当时在Scrapy和requests之间纠结过最后给了个折中方案主链路用Scrapy框架来做分布式爬虫因为它的Downloader Middleware、Item Pipeline、以及对接Scrapy-Redis做分布式都非常成熟但某些动态渲染页面比如接口里嵌了JS反爬参数就用requests Selenium做辅助采集。采集流程上我不建议直接一股脑去请求HTML页面最优雅的做法是先抓接口。很多招聘网站的移动端H5页面或APP接口返回的就是JSON直接从接口拿结构化数据远比你用XPath解析HTML靠谱得多。比如我抓的某招聘网站列表接口返回数据里本身就带了岗位ID、标题、薪资范围字段是15-20K这种字符串、公司名称、学历要求、经验要求、工作地点、发布时间戳几乎就是数据库表结构了。采集完成后的Pipeline清洗很重要。Python中XPath提取需要注意text()函数和string(.)的区别它们返回的内容格式可能会有细微差异。比如用XPath提取岗位描述时如果直接//div[classjob-desc]/text()可能取不到完整内容因为描述文本分散在各层级节点里正确写法是用.string(.)或者normalize-space()。我在这个坑上浪费了大半天做Scrapy的同学建议直接把Item Loader里加一层自定义Processor统一做空格清理和HTML标签剥离。清洗规则我的经验是这几条优先级最高薪资字段统一处理把面议标注为NULL把15-20K拆成min_salary15000和max_salary20000两个字段方便后续推荐系统做数值运算。岗位标签去重从标题和描述里用正则抽取出兼职实习日结周结等关键词放到tags字段里。发布时间标准化转成时间戳超过90天的数据直接丢弃或降权。公司名去重很多公司一个岗位重复发帖用岗位ID公司名做MD5生成唯一业务主键。2.2 反爬机制应对与合规边界爬虫最头疼的就是反爬。我实际遇到的Boss直聘和智联的反爬策略简单总结就是Header检测User-Agent、Referer、IP访问频率检测、Cookie验证特别是伯乐在线那类要携带安全的Cookie、字体反爬数字被自定义字体编码、以及部分接口的参数签名参数位置带有token或sign。应对的基础思路是常规的维护一个User-Agent池随机切换最好混入不同操作系统的UA。维护一个代理IP池我当时用了免费隧道代理每次请求随机选IP控制到每个IP每秒请求不超过1次。用Cookie池管理登录态过一段时间就重新登录换Cookie。解析字体反爬比较麻烦需要下载页面上的woff字体文件把字形映射关系拿下来做解密。这个步骤我只对某一个目标站点做了其他站点如果遇到直接放弃——毕竟项目重点是整个平台不是被单个反爬拖死。定时任务用APScheduler或者系统crontab凌晨2点到6点访问量低、反爬策略松是批量抓取的好时机。再强调一个很多同学会忽略的合规问题。抓取公开信息本身不违法但如果抓取的是用户个人敏感信息比如简历里的手机号、姓名、身份证或者被抓取平台在robots协议里明确禁止爬取那风险就完全不一样了。我当时的做法是只抓公开的岗位招聘信息不抓人和个人简历并且在论文的法律与伦理小节里专门写了《网络安全法》《个人信息保护法》和robots协议相关的内容。这个细节答辩时是加分项老师会觉得你考虑得周全。2.3 SpringBoot服务端如何防止被爬这个点有意思因为搜索热词里反复出现java controller层如何防护防止爬虫。我们的系统自己是一个爬虫聚合者但同时我的平台也怕别人来爬我。所以SpringBoot这一侧的反爬设计也做了这几个手段性价比最高网关层拦截识别高频访问IP用Redis记录每分钟访问次数超过阈值直接返回403。这里我写了一个简单的HandlerInterceptor配合SpringBoot内置的spring-boot-starter-data-redis就能实现不用引入额外的Sentinel等重组件。接口签名校验给前端分配AppID Secret前端调用接口时用HMAC-SHA256对参数时间戳nonce生成签名后端校验。防的不是爬虫脚本而是防止别人直接伪造请求。对毕设来说做一版去掉重放攻击校验的简化签名即可。敏感接口加验证码比如登录、注册、密码找回等接口引入hutool-captcha生成算术验证码防止批量注册机器人。其实防爬和爬之间本身就是矛和盾的关系做这个系统的过程中你既能当矛又能当盾这种双向视角是答辩时可以好好讲的亮点之一。3. Hadoop大数据环境的搭建与实践3.1 伪分布式与集群部署决策按标题的要求Hadoop是大数据底座。对于单机毕设环境我首推的是伪分布式模式也就是一个节点上同时运行NameNode、DataNode、ResourceManager、NodeManager。如果学校实验室机器配置足够再扩展成三节点的完全分布式也来得及因为配置文件改动很小就是core-site.xml、hdfs-site.xml、yarn-site.xml三个文件里的主机名和端口需要调整。搜索热词里总是出现hadoop伪分布式搭建我可以给你一份当时实测可行的最小步骤清单以Hadoop 3.3.5 JDK8为例准备Linux环境我用的CentOS 7虚拟机配置静态IP、hostname映射比如192.168.10.10 hadoop-node。安装JDK8并配置JAVA_HOME环境变量编辑/etc/profileHadoop 3.x对JDK8是原生支持的。下载Hadoop tar包解压到/opt/hadoop配置core-site.xml指定HDFS的NameNode地址和临时文件目录。配置hdfs-site.xml设置副本数伪分布式下副本数必须设为1否则会一直报块副本不达标。配置yarn-site.xml指定ResourceManager地址配置mapred-site.xml指定MapReduce使用Yarn调度。配置workers文件3.x叫workers2.x叫slaves写入本机hostname。关键步骤ssh localhost免密登录配置用ssh-keygen -t rsa生成密钥并拷贝到authorized_keys否则启动时会让你反复输入密码。第一次使用必须执行hdfs namenode -format格式化元数据只能执行一次第二次格式化会污染NameNode的元数据导致DataNode集群ID不一致、启动失败。分别执行start-dfs.sh和start-yarn.sh用jps命令检查进程看到NameNode、DataNode、ResourceManager、NodeManager四个进程就说明启动成功。我遇到的第一个大坑就是Windows下Hadoop环境变量问题很多新手会用Windows直接跑Hadoop但Hadoop在Windows下需要额外配置winutils.exe到bin目录否则会产生各种权限怪异问题。建议直接开个虚拟机或者WSL2省掉一堆事。第二个坑是端口冲突Hadoop 3.x的NameNode Web管理端口已经改成9870了老教程只写50070很多人大半天发现页面打不开其实就是端口变了。还有一次我遇到NameNode无法启动看日志发现dfs.namenode.name.dir路径下的元数据被之前实验污染了解决方式是把/opt/hadoop/tmp/dfs/name/current下的文件删掉重新格式化。所以做实验要养成一个重要习惯每次重新格式化之前把/tmp下的Hadoop数据目录清干净不然集群ID对不上必然出错。3.2 SpringBoot如何对接HDFS做平台最关心的是SpringBoot怎么读写HDFS。我不会去引入复杂的HDFS客户端API而是直接用WebHDFS的REST接口。因为Hadoop原生Java API需要把hdfs-site.xml和core-site.xml都放到classpath里而且需要引入一堆依赖包在项目中维护起来非常繁琐。WebHDFS的方式就清爽很多使用HTTP协议完成文件操作# 创建目录 curl -i -X PUT http://hadoop-node:9870/webhdfs/v1/user/hadoop/兼职数据?opMKDIRS # 上传文件 curl -i -X PUT -T positions.json http://hadoop-node:9870/webhdfs/v1/user/hadoop/positions.json?opCREATE # 查看目录列表 curl http://hadoop-node:9870/webhdfs/v1/user/hadoop/?opLISTSTATUSSpringBoot里我写了一个HdfsService内部用RestTemplate包装这些REST调用上传和下载都能跑通。差不多就相当于把HDFS当作一个大网盘爬虫产出的是文件平台和Hive消费的是文件夹。如果数据量再大一些、想实现增量上传可以用Hadoop的distcp命令在集群之间做数据复制参数上重点记住-m指定map数、-update做增量同步、-delete删除目标端多余文件这三个这也是搜索热词里hadoop面试题的高频考法。不过毕设阶段只要展示出SpringBoot WebHDFS能通就足够在答辩时讲出亮点了。3.3 大数据分析Hive离线指标与数据大屏除了存储大数据环节还要让数据说话。我引入Hive做离线数据分析因为Hive的SQL写法直观而且不熟悉MapReduce的同学也能快速上手。我把清洗后的兼职数据从HDFS导入Hive外部表然后在Hive里写了几条统计SQL产出这几个方向的报表按城市统计兼职岗位数量分析岗位地域分布热力图。按薪资区间统计岗位数量分析兼职薪酬分布。按岗位类型线上兼职、线下兼职、促销导购、家教培训等统计发布数量。按发布时间戳统计每天发布量分析兼职信息发布的时间规律很多公司喜欢在晚上或周末集中发布这个发现我写进了论文非常有生活感。分析结果用sink to hdfs或者直接查出来导出再定时任务同步到MySQL的统计宽表里。Web端的数据大屏基于ECharts实现中国地图展示各城市岗位数柱状图展示薪酬分布折线图展示一周发布量趋势。这块我花了两天时间就搞定了但效果非常好答辩时大屏一展示视觉冲击力直接拉满。建议你也做一个哪怕数据量少视觉效果和数据的故事感是加分利器。4. AI大模型驱动的个性化推荐实现4.1 推荐系统的整体流程个性化推荐是另一个核心点。我的最终推荐流程是粗排召回 精排打分两步走第一步建立候选集。基于用户行为日志收藏、浏览、投递做协同过滤。ItemCF基于物品的协同过滤的核心思路是找与用户看过岗位相似的其他岗位。相似度计算用的是余弦相似度公式把每个岗位的标签属性向量化然后计算余弦值。示例的简化版本是这样的# 计算两个岗位标签集合的Jaccard相似度简化版 def calc_similarity(job_a_tags, job_b_tags): set_a set(job_a_tags) set_b set(job_b_tags) inter len(set_a set_b) union len(set_a | set_b) return inter / union if union 0 else 0第二步精排打分。把候选岗位的标题、描述、标签加上用户的简历标签和浏览历史描述拼接成一段结构化文本交给AI大模型打分。这一步是为了解决纯标签匹配的语义鸿沟问题比如用户的标签里有英语家教岗位描述是辅导中小学生英语要求四六级通过传统标签匹配很难算出来相关性但大模型能理解这两段文本在语义上是高度匹配的。4.2 大模型选型与本地部署经验搜索热词里有ai大模型本地部署配置ai大模型基础理论这块我也踩了不少坑。在线调用文心一言或通义千问API效果很稳定代码也简单缺点是答辩时如果网络环境不好演示会翻车。本地部署则更可控我用的方案是Ollama qwen2.5:7b或者nlpcc的轻量模型7B参数量化版的显存需求大概6GB左右笔记本的RTX 3060就能跑。如果你的机器没独立显卡直接用纯CPU推理也可以但响应速度会慢很多需要把并发调低、输出限制到较短长度。设置上可以用OLLAMA_MAX_LOADED_MODELS2、OLLAMA_NUM_PARALLEL1这类环境变量来管理服务资源。SpringBoot调用本地大模型服务的方式非常简单本质就是HTTP请求localhost:11434/api/generate接口传一个JSON promptString prompt 岗位标签 job.getTags() 岗位描述 job.getDescription() 用户标签 user.getTags(); // 通过RestTemplate调用Ollama大模型返回的是打分结果比如9分用一个JSON解析层取出来再和协同过滤的候选集做一个加权融合最终得分 0.4 * 协同过滤相似度 0.6 * LLM语义打分。后面这个权重是可以调的这也是系统里一个有意思的实验点。在写Prompt的时候有个很大的感受指令必须清晰格式要能让模型稳定输出。我当时用的Prompt模板大概是这样的你是一个兼职岗位匹配专家。根据用户画像标签与岗位描述判断该用户与岗位的匹配程度。 用户标签{user_tags} 岗位描述{job_desc} 请输出一个0到10的整数作为匹配度分数不要输出其他任何内容。如果不强调不要输出其他任何内容大模型会给你回一大段分析文字解析逻辑就非常难写。4.3 为什么不用纯推荐算法偏要引入大模型我答辩时被问到过这个问题协同过滤已经很经典了你为什么要额外搞一个大模型打分我的回答思路是这样的协同过滤推荐依赖用户行为历史有冷启动问题——新用户没有浏览记录协同过滤无法推荐。而大模型推荐是内容理解的推荐即使没有历史行为只给用户一段简历文本标签大模型依然能理解用户是计算机专业学生、会Python和岗位是爬虫开发助理之间的匹配关系。两条技术路线互为补充正好覆盖了不同场景。这个解释有深度也体现了两套技术路线的洞察力建议写到论文的创新点里。5. SpringBoot业务层与前端联调的关键细节5.1 前端Vue项目如何放进SpringBoot很多同学的毕设前端是Vue写的但答辩演示时希望能一键启动不要求同时开两个服务。这里搜热词里提到的vue打包放进springboot中是个非常常见的需求。做法不复杂先在Vue项目的vue.config.js里设置publicPath: ./否则打包后的静态资源路径是绝对路径/js/xxx.js部署到SpringBoot的static目录后会404。然后执行npm run build把dist目录下的内容复制到SpringBoot的src/main/resources/static里。如果用的是前后端分离开发模式记得给Vue配置开发环境的proxy代理上线部署时把axios的baseURL改成相对路径/api。后端接口统一以/api开头然后在Controller里映射。这样打出来的jar包访问http://localhost:8080时SpringBoot会自动把static目录下的index.html作为首页返回。到这里一个打包完毕可直接运行的完整系统就诞生了答辩演示非常吃这套。5.2 SpringBoot配置和启动端口的几个坑搜索热词里提到springboot版本太高idea 2026怎么配置springboot服务启动端口。我建议2025年做毕设的同学没有特殊需求就用SpringBoot 2.7.x它和MyBatis-Plus、Hutool、JWT等生态的兼容性都是验证过的。SpringBoot 3.x默认使用Jakarta命名空间很多老教程中javax.servlet代码全部要改加上JDK版本要求高容易在环境配置上消耗大量时间。如果只是想修改启动端口在application.yml里配置server: port: 8081用IDEA运行时可以在Run Configuration的Environment variables里新增SERVER_PORT8082这个配置优先级高于yml文件。两种情况我都用过实际开发中还是用yml最省事写进代码版本仓库也不容易丢。5.3 Controller层防护与接口设计的总结通过爬虫技术上自己系统内部也在做防护前面2.3节提到过了。简单来说核心是在拦截器里做一个IP频率计数器加上接口签名防篡改以及给admin接口加简单的token校验。对于毕设来说做到这个程度已经很够了没必要再上防重放、风控那种企业级方案否则工作量直接爆炸。6. 常见问题排查与答辩高频问答实录我把这个项目从搭建到跑通踩过的坑整理成了一张表做毕设的同学可以对照着排查问题现象可能的根因解决方式Hadoop的NameNode启动失败nameNode元数据被污染、端口被占用清空tmp目录重新格式化NameNode用jps检查进程HDFS上传文件报集群ID不匹配多次格式化导致DataNode的ClusterID和NameNode不一致删除DataNode数据目录下的VERSION文件后重启SpringBoot 3.x无法引入旧版依赖Jakarta命名空间变更换SpringBoot 2.7.x或将javax替换成jakartaVue打包后页面空白静态资源路径是绝对路径设置publicPath: ./重新构建大模型推理OOM量化级别太高或并行线程过多使用q4_k_m量化、设置OLLAMA_MAX_LOADED_MODELS爬虫被目标站点封IP请求频率过高、无代理加随机延时、代理池、Cookie池Hive分析结果和预期差很多原始数据中脏数据太多增加清洗Pipeline去除空岗位、重复岗位、过期岗位关于Hadoop面试题答辩老师几乎必问的还有这些HDFS的读写流程NameNode和DataNode的区别MapReduce的Shuffle阶段在做什么为什么MapReduce计算慢Hadoop与Spark/Flink的差别。我当时为了答辩专门准备了这几个问题的标准回答HDFS写文件流程客户端请求NameNode创建文件NameNode返回可用DataNode节点列表客户端分块写入DataNodeDataNode之间做副本复制写完通知NameNode更新元数据。核心就是元数据与数据分离。Shuffle阶段Map端输出结果先分区排序写本地磁盘Reduce端拉取自己负责分区的数据做归并排序再进行reduce计算。之所以慢在于中间结果要频繁落盘IO开销大。Hadoop与Spark的区别Spark基于内存计算适合迭代式算法如ALS推荐Hadoop的MapReduce每个任务都要读写磁盘。但是Hadoop生态成熟、运维稳定离线数仓场景仍然是主流选择。为什么这个项目用Hadoop而不是Spark我答离线批处理数据归档场景Hadoop的稳定性和生态优势更明显且当前数据量远不到计算瓶颈。这几个问题准备好了基本能应对老师关于大数据部分的连环追问。7. 论文与答辩PPT的素材组织既然标题里写了精品论文答辩PPT这块我也给你一些实战建议帮你减少整理工作量。论文的章节建议按照这个顺序来写摘要与关键词 → 绪论选题背景、国内外研究现状、论文组织结构 → 相关技术介绍SpringBoot、Hadoop、爬虫技术、AI大模型 → 系统需求分析可行性分析、功能需求、非功能需求 → 系统设计总体架构、功能模块设计、数据库设计 → 系统实现每个模块的核心代码与截图 → 系统测试功能测试用例表、性能测试 → 总结与展望。写论文最花时间的其实是截图和测试用例表。我当时先做了个硬性规定每一个功能模块必须配一张截图一段实现说明攒下来二十多张图后面论文排版时基本就是按图说话。测试部分做一张大表测试编号、测试功能、操作步骤、预期结果、实际结果、是否通过。老师收到这样的论文第一感觉就是工作量非常饱满。PPT的页数控制在10页左右封面 → 目录 → 选题背景与意义 → 核心技术与选型对比 → 系统架构图与功能模块图 → 数据库设计 → 核心功能演示截图重点放数据大屏和推荐页面 → 个性化推荐算法流程协同过滤大模型打分 → 测试结果 → 总结与展望。其中系统架构图这一页是你整个汇报的锚点其他页都可以围绕它展开。答辩演示时两个最容易翻车的地方一是爬虫演示时目标网站页面结构变了或者你的IP被封了导致现场采集不到数据所以一定要提前录制一段爬虫运行视频作为plan B二是本地大模型推理速度太慢推荐结果迟迟刷不出来解决方法是提前在Redis里缓存一份当前演示用户的推荐结果演示时点击按钮直接读缓存秒开。最后分享几个我自己做过之后的实在体会这个项目真正跑完之后我最大的感受是它不是五个独立技术的堆积而是把数据如何从无到有、从有到有效、从有效到智能这条链路完整地走了一遍。你既要懂爬虫的规则和反爬的套路也要懂HDFS的架构和Hive的分析写法还要懂大模型Prompt的构造和推荐算法的融合逻辑。任何一环出现薄弱整个系统都会掉链子。经验上特别想提醒一句不要在技术选型的炫技上花太多时间。很多同学看了一些企业级架构的帖子就总想在自己毕设里塞Flink、Kafka、Doris塞到最后每个组件都只是跑通了一个Demo根本不知道自己解决了什么问题。而这个项目的正确打开方式恰恰相反——用尽量成熟简单的方式把爬虫采集、大数据存储分析、AI推荐这个小闭环讲清楚对找工作面试来说比展示一堆装点门面的组件名有用得多。还有一个小技巧做答辩PPT的时候把系统里的数据大屏截图放在核心功能演示的第一页因为它最直观、最有画面感。老师看了单调的列表页面一整场突然出现一个带中国地图热力分布的大屏注意力马上就回来了。这个细节我至今觉得是整场答辩最值得的一笔投入。