ARTICLE DETAIL

资讯详情

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

基于用户行为的租房推荐系统与可视化平台:Python毕设全链路实现

基于用户行为的租房推荐系统与可视化平台:Python毕设全链路实现 每年毕业季计算机专业总有那么一批人被毕业设计折腾得够呛。如果你正在研究“Python基于用户行为的租房推荐与可视化平台”我建议你把这个方向吃透——它不只是做一个CRUD管理系统而是把Scrapy爬虫、推荐系统、大数据清洗、可视化几大核心考察点全部串在了一个有真实业务场景的项目里。这篇内容我会从项目怎么立项、系统怎么架构、爬虫怎么写、推荐算法怎么落地、可视化大屏怎么搭一直讲到底层踩坑经验适合想做全栈型毕设、又不想堆砌功能的同学收藏后一步一步复现。这个项目解决的痛点其实很直接传统租房平台房源多、筛选条件机械用户找到合适房子的成本很高。而基于用户行为构建推荐能根据点击、收藏、搜索记录找到潜在偏好把“人找房”变成“房找人”。从毕业设计角度来说它完整覆盖了数据采集、算法应用、数据可视化的全链路答辩时能讲的东西非常多代码量也足够展示工作量。下面我就把自己带过这类项目的经验和复盘整理出来按模块拆开讲。1. 项目概述与核心价值1.1 选题背景与毕设定位租房推荐系统的核心难点在于“房源不像商品地域属性极强用户决策链条长”。你买一件衣服可能看了三次就下单但租一套房子用户会关注位置、价格、户型、交通、周边配套而且行为分散在浏览、收藏、对比、联系中介等多个环节。所以这个选题把“用户行为”作为推荐依据天然比普通商品推荐更有解释空间。从毕设验收角度这个题目有几个好处第一数据是自己爬的避开了“数据集从哪里来”的尴尬问题第二推荐系统可以简单做也可以深入做能匹配不同学校的要求第三可视化大屏容易出效果答辩演示时非常直观。很多学校的毕设还要求体现“大数据”或“智能分析”这个项目正好能把这些词落到实处而不是只写在论文里。1.2 技术栈选型与核心能力拆解我推荐的技术栈如下比较稳遇到问题社区资料也多。模块技术选型负责内容数据采集Scrapy Requests抓取租房平台房源信息、用户行为模拟数据存储MySQL Redis结构化数据存储、推荐缓存与热门列表数据分析Pandas NumPy数据清洗、行为日志聚合、均值/分布计算推荐算法Python 实现协同过滤 混合策略相似度计算、召回、排序、冷启动后端接口Flask 或 Django提供房源、推荐、行为上报等 REST 接口可视化PyECharts / ECharts HTML大屏图表、地图热力图、指标卡进阶亮点Sentence-BERT / LLM API房源文本语义相似度、推荐理由生成这套选型背后有我的考虑Scrapy 负责批量房源采集Redis 缓存热数据减少数据库压力Pandas 做离线分析和生成推荐结果Flask 只做轻量接口服务。如果学校要求“大数据”技术栈可以额外引入 Spark 做离线分析或者用 Hive 建表统计但实际毕设数据量一般不到百万级Pandas 完全够用没必要一开始就把 Hadoop 全家桶搬上来。2. 系统整体架构与数据流向2.1 分层架构设计思路我在设计这个项目时把整个系统分成了四层每一层只干一件事层与层之间通过接口或数据库解耦这样后面调试和写论文都非常舒服。采集层Scrapy 爬虫定时从租房网站采集房源标题、价格、户型、面积、区域、经纬度、图片地址、发布时间等字段写回 MySQL。服务层Flask/Django 提供 Web 接口包括用户浏览房源、上报行为、获取推荐列表、查询可视化数据。算法层离线脚本读取行为表和房源表训练或者计算相似度矩阵生成每个用户的 TopN 推荐结果也负责新用户冷启动。展示层前端页面调用接口渲染列表和可视化大屏。大屏数据不是前端自己算而是后端接口聚合好返回前端只负责画图。这样设计的最大好处是爬虫挂了不影响推荐逻辑推荐脚本跑挂了不影响 Web 端显示历史推荐。毕设答辩的时候如果有人问“系统如何扩展”你可以直接说把算法层拆成独立服务换成 Spark 或者上线推荐引擎就行话术非常顺。2.2 数据库表结构与关键字段数据库是业务的基础我建议至少设计四张核心表。不要觉得简单字段设计是否合理直接决定后面推荐算法好不好写。用户表 userCREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64), city VARCHAR(32), create_time DATETIME );房源表 houseCREATE TABLE house ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255), price DECIMAL(10,2), area DECIMAL(8,2), room_num INT, hall_num INT, district VARCHAR(32), address VARCHAR(255), longitude DECIMAL(10,6), latitude DECIMAL(10,6), tags VARCHAR(255), description TEXT, crawl_source VARCHAR(32), crawl_time DATETIME );用户行为表 behaviorCREATE TABLE behavior ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, house_id INT, behavior_type VARCHAR(16), -- browse, favorite, contact behavior_time DATETIME );推荐结果表 recommend_resultCREATE TABLE recommend_result ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, house_id INT, score DECIMAL(5,4), reason VARCHAR(500), create_time DATETIME );这里的核心是behavior表。我见过很多同学只存了“用户收藏了哪套房”这是不够的。浏览、点击、收藏、联系中介这些行为价值不同推荐时应该给不同权重。比如浏览权重1收藏权重5联系方式等深度行为权重10。这一细节在论文里写清楚就能多出几百字的工作量。2.3 数据流与模块解耦完整的数据流大概是这样的爬虫采集房源 → 清洗后写入 house 表 → 前端页面加载房源详情 → 用户点击/收藏/联系时向后端上报行为 → 后端写 behavior 表 → 定时任务执行推荐脚本 → 脚本读取 house 和 behavior计算推荐结果写入 recommend_result 表 → Web 接口读取推荐结果展示给用户。我把这个流程重点解释一下“为什么用定时任务而不是每次实时计算”协同过滤需要全量算相似度如果用户每次打开页面都实时算数据库会被拖垮接口也容易超时。离线算好结果存表在线只做读操作性能稳定得多。毕设虽然数据量不大但养成这种工程习惯答辩时很有说服力。3. Scrapy爬虫采集房源数据3.1 爬虫项目结构与运行入口Scrapy 项目我建议按标准结构建不用改太多但命名要清晰。rental_spider/ ├── scrapy.cfg ├── rental_spider/ │ ├── __init__.py │ ├── items.py │ ├── middlewares.py │ ├── pipelines.py │ ├── settings.py │ ├── spiders/ │ │ └── house_spider.py │ └── run.pyrun.py不是 Scrapy 自动生成的我会加一个这个文件里面写CrawlerProcess方便调试。因为直接在终端执行scrapy crawl house虽然也可以但用 PyCharm 调试时run.py能给你断点排查问题舒服很多。from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from rental_spider.spiders.house_spider import HouseSpider process CrawlerProcess(get_project_settings()) process.crawl(HouseSpider) process.start()这个爬虫不需要登录只要处理好反爬和时间间隔能稳定抓到大量房源数据。如果目标站点是动态渲染的再用 Selenium 或者 Playwright 补充但租房平台一般都有静态页Scrapy 足够。3.2 Item字段与Pipeline清洗流程在items.py里定义要采集的字段我建议字段别太杂够用就行防止后面清洗麻烦。import scrapy class HouseItem(scrapy.Item): title scrapy.Field() price scrapy.Field() area scrapy.Field() room_num scrapy.Field() hall_num scrapy.Field() district scrapy.Field() address scrapy.Field() tags scrapy.Field() description scrapy.Field() house_url scrapy.Field()Pipeline 里做两件事一是数据清洗价格字符串去掉“元/月”面积去掉“㎡”户型解析成几室几厅二是去重按照house_url判断是否已存在重复数据直接丢。去重这段代码很简单但非常重要否则爬一次脚本就存一堆重复房源。import pymysql from rental_spider.items import HouseItem class HousePipeline: def process_item(self, item, spider): if not item.get(title): raise DropItem(fMissing title: {item}) # 清洗价格 item[price] float(item[price].replace(元/月, ).strip()) # 清洗面积 item[area] float(item[area].replace(㎡, ).strip()) # 解析室厅 if item.get(room_num): item[room_num] int(item[room_num]) return item这里要提醒一个坑很多租房平台会把价格写成“面议”或“暂无价格”这种数据尽量不要直接转 float不然爬虫会中断。我一般先判断字符串里是否全数字不是就跳过或者置为 0后续推荐时再做过滤。3.3 反爬应对与抓取策略写爬虫最怕封 IP 和验证码。我的建议是访问频率尽量低伪装尽量真实。具体做法包括设置DOWNLOAD_DELAY 1.0每请求之间至少间隔 1 秒随机切换 User-Agent不要总用同一个浏览器头在settings.py中启用 RetryMiddleware出错自动重试两次不要把所有请求都集中在同一个时间点爬虫脚本可以做成每天定时跑增量数据。# settings.py BOT_NAME rental_spider DOWNLOAD_DELAY 1.0 USER_AGENT_LIST [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36, ] RANDOMIZE_DOWNLOAD_DELAY True RETRY_ENABLED True RETRY_TIMES 2有人觉得延迟 1 秒太慢但你想想毕设最多需要几千套房源就算 5 秒一条一万条也才十几个小时挂机跑一天完全没问题。折腾高并发反而容易触发封禁得不偿失。另外建议遵守目标网站的 robots 协议只采集必要的公开信息也不要商用。毕设场景下爬少量数据用于学习展示是完全没问题的但代码里最好加上对来源的标注体现版权意识。4. 基于用户行为的推荐系统设计与实现4.1 用户行为埋点与数据建模数据是推荐系统的基础没有用户行为后面再牛的算法也是空中楼阁。所以我在 Web 端写了一个简单的前端埋点用户详情页停留超过 10 秒上报一个“浏览”行为用户点击收藏按钮上报“收藏”行为用户点击“查看联系方式”上报“联系”行为。后端接口大概这样app.route(/api/behavior, methods[POST]) def behavior(): data request.get_json() user_id data[user_id] house_id data[house_id] behavior_type data[behavior_type] cursor.execute( INSERT INTO behavior(user_id, house_id, behavior_type, behavior_time) VALUES (%s,%s,%s,%s), (user_id, house_id, behavior_type, datetime.now()) ) db.commit() return jsonify({code: 0})有了这些原始行为后我们要转成推荐算法能用的“评分矩阵”。一般方式是把行为按权重映射成分数比如浏览 1 分、收藏 5 分、联系 10 分。如果同一个用户对同一套房有多种行为就取最大值或者加权求和。这个矩阵我建议用 Pandas 生成行为数据量不大内存完全扛得住。import pandas as pd import numpy as np df pd.read_sql(SELECT user_id, house_id, behavior_type FROM behavior, engine) weight_map {browse: 1, favorite: 5, contact: 10} df[score] df[behavior_type].map(weight_map) rating_matrix df.groupby([user_id, house_id])[score].max().reset_index() rating_pivot rating_matrix.pivot(indexuser_id, columnshouse_id, valuesscore).fillna(0)这里我特意用max而不是sum因为同一用户反复浏览同一套房时如果累加分数会虚高推荐结果总是那几套热门房源失去个性化。取最大值能减轻重复行为的影响逻辑上更合理。4.2 协同过滤算法核心实现租房场景我推荐用基于物品的协同过滤ItemCF它的核心思想是如果你喜欢的房子和另一套房子在一部分用户眼里很相似那么你也大概率会喜欢那套。比基于用户的协同过滤更稳定因为房源变化比用户偏好变化慢。计算相似度时我一般用余弦相似度。比如两个房源的被收藏记录是两个向量向量包含所有用户对这两个房源的评分相似度越高说明它们同时被同一批用户喜欢的程度越高。from sklearn.metrics.pairwise import cosine_similarity item_matrix rating_pivot.T # 每行是一个房源每列是一个用户 item_sim cosine_similarity(item_matrix) item_sim_df pd.DataFrame(item_sim, indexitem_matrix.index, columnsitem_matrix.index)得到房源相似度矩阵后给某个用户推荐时找出用户评分高的房源再找出与这些房源相似但用户没接触过的房源按相似度×用户评分加权排序取 TopN。def recommend_for_user(user_id, pivot, item_sim_df, top_n10): user_ratings pivot.loc[user_id] scored {} for house_id, score in user_ratings.items(): if score 0: continue sim_scores item_sim_df[house_id] for candidate, sim in sim_scores.items(): if user_ratings[candidate] 0: scored[candidate] scored.get(candidate, 0) sim * score if not scored: return [] ranked sorted(scored.items(), keylambda x: x[1], reverseTrue) return [house_id for house_id, _ in ranked[:top_n]]代码很简单但效果好不好关键是行为数据够不够。如果每个用户的行为只有个位数算出来的相似度矩阵很稀疏。这时候就要引入下面的冷启动策略。4.3 冷启动问题与混合推荐策略冷启动是推荐系统里被问得最多的问题答辩老师也很爱问。新用户没有任何行为时协同过滤直接失效。所以我会加一个基于规则的冷启动推荐根据用户注册时选择的城市和预算从房源库里筛选符合基本条件的房源再按热度排序补足 TopN。def cold_start_recommend(city, min_price, max_price, top_n10): sql SELECT id FROM house WHERE city%s AND price BETWEEN %s AND %s ORDER BY favorite_count DESC, view_count DESC LIMIT %s rows db_query(sql, (city, min_price, max_price, top_n)) return [r[id] for r in rows]新房源的冷启动是另一个问题。刚爬到的房源没有用户行为协同过滤不会推荐它。我通常的做法是把新发布的房源以一定比例比如10%混入推荐列表保证新内容有曝光机会等积累了行为再进入主推荐流程。这样既能缓解冷启动也让用户感觉产品是“活”的。最终线上推荐采用混合策略有行为数据的用户70% 从协同过滤结果里取30% 从热门和新增房源里取保证推荐的多样性和时效性没有行为数据的用户100% 走规则冷启动。这个策略操作简单效果也比纯算法稳定。4.4 基于大模型辅助推荐的扩展标题里提到“大模型”这里补充一个加分项方案。如果你想让毕设看起来更有亮点可以用文本语义相似度来做房源召回或者用大模型生成推荐理由。具体做法是把房源的标题、描述、标签拼成一段文本用 Sentence-BERT 等预训练模型转成向量然后存到向量数据库或者直接计算余弦相似度。这样用户看中一套房后系统可以找出“描述风格、通勤条件、户型特点”相似的推荐房源弥补行为数据的不足。语义向量的相似度计算对毕设数据量来说完全可行。from sentence_transformers import SentenceTransformer model SentenceTransformer(paraphrase-multilingual-MiniLM-L12-v2) text 两室一厅近地铁精装修押一付一 emb model.encode(text)这个方案不需要你微调模型直接调用现成的编码器就能出结果。它最大的作用是提升推荐理由的可解释性可以给每条推荐生成一句“因为您关注的地段临近地铁这套房源交通评分很高”比单纯算分数更有说服力。如果学校不允许调用外部 API也可以用本地 BERT 模型完全离线运行。当然大模型在毕设里只能作为辅助模块不建议把核心推荐全押在生成式模型上一是答辩稳定性不够二是计算资源可能受限。我见过有同学用 ChatGPT API 给每套房生成推荐文案但 API 调用不稳定演示时断网就尴尬了。所以我更建议协同过滤作为主引擎语义相似度作为召回扩量推荐理由模板大模型润色作为展示亮点。5. 数据分析与可视化大屏搭建5.1 数据分析指标设计可视化不是简单画两个图而是要给业务提供决策信息。我在这个项目里挑选了几个核心指标每个指标背后都有明确的分析目的。房源价格分布理解城市整体租金水平直方图展示价格区间房源数量区域房源热度统计每个行政区房源数量和平均价格热力图展示户型占比饼图/环形图展示一居、两居、三居比例用户偏好分析被收藏最多的 Top10 房源特征比如标签频率排序行为时段分析用户浏览/收藏行为在一天内的分布折线图展示先后高峰。这些指标对推荐结果很有解释力。比如“区域房源热度”高的小区可以作为热门候选“用户偏好分析”发现大部分用户勾选了“近地铁”那推荐时就要给这个标签更高的权重。论文里把指标的定义、计算逻辑、业务含义写出来逻辑闭环就成立了。5.2 ECharts可视化图表实现可视化层我推荐用 PyECharts 生成 HTML再把多个图表嵌入到一个页面里。PyECharts 的好处是可以用 Python 直接操作数据不用写一堆 JavaScript。比如绘制区域房源数量柱状图from pyecharts.charts import Bar from pyecharts import options as opts def district_bar(): data db_query(SELECT district, COUNT(*) cnt FROM house GROUP BY district) bar ( Bar() .add_xaxis([x[district] for x in data]) .add_yaxis(房源数量, [x[cnt] for x in data]) .set_global_opts(title_optsopts.TitleOpts(title区域房源数量分布)) ) return bar.render_embed()后端接口把生成的图表 HTML 片段返回或者直接生成完整静态页。如果学校要求可视化大屏就需要把多个图表拼在一个页面里用 CSS Grid 布局再添加定时刷新机制。不过毕设演示一般不需要实时刷新定时刷新反而容易造成页面闪烁。地图热力图是另一个值得做的亮点推荐用 ECharts 的百度地图或高德地图组件。不过调用地图组件需要申请 Key而且有些环境离线跑不了所以做之前要确认演示现场的网络条件。如果没网用区域柱状图代替也完全够用。5.3 可视化大屏布局与性能优化大屏布局我一般分成三块顶部放核心 KPI 卡片总房源数、全城均价、热门区域、活跃用户数中间区域放地图热力图和价格分布底部放排行榜和户型占比。整体色调建议统一学术风格偏深色背景视觉冲击力强答辩现场容易抓眼球。性能优化方面有两个重点一是图表数据从后端聚合好再返回不要让前端从明细表里做 join 和 count二是统计结果缓存到 Redis设置 10 分钟过期因为大屏数据不需要秒级更新。cache_key dashboard_metrics metrics redis.get(cache_key) if not metrics: metrics compute_all_metrics() redis.setex(cache_key, 600, metrics)如果你发现大屏加载很慢90% 是 SQL 写得不够好。建议在behavior表和house表上给常用查询字段加索引比如user_id、house_id、district、price。加了索引后几十万数据量的聚合查询可以从秒级降到毫秒级这个优化你写在技术文档里会非常加分。6. 端到端实现与部署实践6.1 环境准备与依赖安装这个项目运行环境我没有用特别新的版本稳定为主。推荐 Python 3.9 或 3.10避免某些第三方库在新版本上还没适配。安装依赖的命令很简单使用 requirements.txt 统一管理pip install scrapy pymysql pandas numpy scikit-learn flask pyecharts redis如果装 scrapy 时遇到编译问题Windows 上尤其容易因为缺少 C 构建工具报错。我的建议是直接使用 Anaconda 创建虚拟环境conda 装的 scrapy 通常更省心conda create -n rental_env python3.9 conda activate rental_env pip install -r requirements.txt安装完后到 MySQL 里创建数据库导入上面设计的表结构。注意 MySQL 字符集建议用 utf8mb4因为房源标题和描述里经常有特殊字符和 emojiutf8mb4 才能存下。这里顺便提醒不要使用 emoji 做任何标注但在数据字段中要考虑到它们。6.2 爬虫运行与数据入库启动爬虫前先确保数据库连接配置正确。我一般把数据库连接信息写在一个db_config.py里PyMySQL 读取使用。不要到处硬编码后续维护会想骂人。import pymysql def get_conn(): return pymysql.connect( hostlocalhost, userroot, password123456, databaserental_db, charsetutf8mb4, cursorclasspymysql.cursors.DictCursor )准备好后运行爬虫cd rental_spider python run.py如果一切正常控制台会不断输出抓到的房源信息抓取结束后可以用一条 SQL 验证入库数量SELECT COUNT(*) FROM house;我建议抓数据的时候分区域爬比如一次只跑一个城区的列表页这样反爬压力小也方便排查哪个区域数据缺失。第一次跑不一定求多500 套以上就能满足后续算法和可视化演示需求。6.3 推荐服务与Web端联调爬完数据后先执行推荐脚本生成推荐结果再启动 Flask 服务。推荐脚本一般单独跑python run_recommend.py脚本逻辑是读取所有用户行为计算相似度生成每个用户的推荐结果写入recommend_result表。可以动态传入参数比如只对有评分的用户计算没有行为的用户留空等冷启动接口动态生成。然后启动 Web 服务python app.py浏览器访问http://127.0.0.1:5000首页展示所有房源列表和推荐位。前端工程我建议用最简单的 Flask 渲染 HTML 或者直接返回 JSON如果写纯前后端分离需要装 Node.js反而增加复杂度。毕设只要功能完整、演示流畅即可不用刻意追求微服务架构。联调的时候验证几个关键链路点击房源详情是否会写入行为记录行为积累之后推荐位顺序是否会变收藏过的房源是否会出现在推荐理由里。把这三个链路跑通核心功能就稳了。7. 常见问题与排查技巧实录7.1 Scrapy抓不到数据怎么办爬虫跑起来返回 200 但 items 是空的这是我在带项目时遇到最多的问题。绝大多数原因是 CSS 或 XPath 选择器没匹配到真实页面结构。排查方法有两种一是用 Scrapy Shell 手动请求一个列表页逐个测试选择器表达式二是直接在代码里输出 response.text 前 1000 个字符看页面是否经过 JS 动态渲染。如果确认是动态渲染要么换接口地址要么引入 Playwright但最省事的做法是找列表页背后的 JSON 接口Scrapy 请求 JSON 解析反而更简单。7.2 推荐结果不理想很多同学跑完推荐后反馈推荐出来的房子全是热门房源看不出来个性化。原因很简单用户行为太稀疏或者相似度矩阵计算太粗。我的调试步骤是先检查rating_pivot的稀疏度如果 80% 以上是 0说明行为数据量不足可以降低行为权重或者改用基于内容相似度补足。另一个原因是没有过滤用户已经看过的房源推荐前必须排除行为表中已有的房源否则老是把用户点过的放前面观感很差。7.3 可视化大屏加载卡顿大屏页面经常一打开就转圈。首先看是不是请求接口太多一个页面 8 个图表并 8 次请求后端 MySQL 扛不住就慢。解决办法是合并接口一个/api/dashboard返回所有图表数据。其次看 SQL 是否有全表扫描记得加索引。最后如果数据量大图表聚合可以提前到离线统计把结果跑成一张dashboard_summary表大屏直接查这张表。实测下来这三种优化做完加载速度能从几秒降到几百毫秒。7.4 大数据量下的性能优化建议毕设数据量不大但答辩老师可能会问“如果数据量翻 100 倍怎么办”。这个问题的标准回答是引入分布式存储和计算。从 MySQL 换到 Hive 做离线统计推荐脚本用 Spark 跑Redis 加缓存。你不需要真的部署一套 Hadoop只要把这个演进思路写在论文的技术展望里就行。当然如果你有能力在虚拟机里装一个单机伪分布式 Hadoop跑一次最简单的统计任务那答辩效果就更扎实了。最后分享一点个人体会做这个项目最忌讳的是把所有代码写完再一次性调通。我的做法是先把爬虫跑通存 200 条数据然后直接写可视化看效果有了数据集再回头写推荐算法对比不同参数下的推荐结果。一步一步来每步都能看到反馈出了问题也知道该查哪层。要是你正卡在某个环境安装或者算法计算里不妨把项目拆细一点逐个击破。真正做完你会发现这个选题不仅毕设稳了简历上也能多写一个“完整推荐系统落地经验”非常划算。
返回列表