尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

租房数据分析毕设:从Python爬虫到Hadoop+Vue可视化

租房数据分析毕设:从Python爬虫到Hadoop+Vue可视化 简介这是面向计算机相关专业毕业生的毕业设计论文资源主题为基于Python、Hadoop、Flask与Vue的租房数据分析系统覆盖毕业设计与毕业论文写作双重需求。文档为docx格式压缩包共1个文件约6.34MB为可直接编辑的Word版论文内容完整包含摘要、目录、绪论、系统关键技术、功能模块与数据分析等章节。已有153人学习浏览适合作为选题参考、项目开发或论文结构设计的样例。文中以Hadoop大数据平台为底层支撑结合Django/Flask后端、MySQL存储与Vue前端系统阐述管理员端和前台端各模块的划分方式、数据管理流程及实现效果并具体涉及HDFS、MapReduce等核心技术。读者既能快速把握租房数据分析系统的整体设计方案又能借鉴其中关于大数据处理、Web开发与论文撰写的组织思路为完成类似课题提供直接帮助。1. 毕业设计选型租房数据分析先想清楚论文怎么讲每年毕业季都有大量“XX数据分析系统”选题租房方向因为数据易得、业务直观、可视化效果好一直是热门选型。但很多同学把时间花在爬虫和前端页面上答辩时讲不清 Hadoop 到底做了什么反而被评委问住。这篇博文按“Python 采集清洗 → Hadoop 离线存储与计算 → Flask 提供接口 → Vue 可视化”这条最稳妥的链路把每个环节的关键代码、参数和坑位一次说透。这套四层架构要解决的核心问题很简单租房数据量不大但来源杂、字段乱、需要反复分析Hadoop 的价值在于把数据从“抓下来能用”变成“存得住、算得动”Flask 和 Vue 则是把分析结果变成可交互的展示。适合正在做毕设、需要快速跑通并可演示的读者也适合想了解小规模数仓怎么落地的从业者——架构可以缩思路不能省。2. Python 采集与预处理别让脏数据毁掉整个分析2.1 爬虫的目标与反爬边界租房数据的常规来源是链家、贝壳、安居客这类公开房源页面。常见做法是用 Python 的requests加BeautifulSoup或parsel抓取列表页和详情页重点提取小区名、区域、户型、面积、朝向、楼层、租金、发布时间、经纬度这几个字段。抓取要用time.sleep()控制频率随机 User-Agent必要时用cookies维持会话规模控制在几千到一两万条即可毕设不需要全量数据重在把链路打通。import requests import time import random from bs4 import BeautifulSoup import pandas as pd headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } base_url https://xxx.zu.ke.com/zufang/pg{}/ data_list [] for page in range(1, 51): url base_url.format(page) try: resp requests.get(url, headersheaders, timeout10) soup BeautifulSoup(resp.text, html.parser) for item in soup.select(.content__list--item): title item.select_one(.content__list--item--title).text.strip() detail item.select_one(.content__list--item--des).text.split( / ) price item.select_one(.content__list--item-price).text.strip() data_list.append([title, detail[0], detail[1], price]) except Exception as e: print(f第{page}页失败: {e}) time.sleep(random.uniform(1, 3))这段代码的逻辑是遍历前 50 页列表页每页解析标题、区域描述和价格三个核心信息单页失败直接跳过不影响整体。time.sleep(random.uniform(1, 3))是请求间隔单位是秒取值在 1 到 3 秒之间随机避免被服务器识别为高频请求如果目标网站的验证码校验严格timeout参数可以缩小到 5 秒快速失败及时切换代理。实际项目中detail[0]通常对应区域和板块detail[1]对应户型信息字段具体位置要看页面结构微调。采集完成后第一件事不是入库而是先导出成 CSV 看一眼字段长什么样。常见问题是租金“4500元/月”带着单位、面积“45㎡”混着汉字、有些房源朝向是 NULL、同一个小区的名字一会儿叫“阳光花园”一会儿叫“阳光花园二期”。这些脏数据不处理后面的 SQL 分析和可视化全部会失真。2.2 用 pandas 做字段归一化与异常过滤数据处理用 pandas 最顺手。核心操作有四类字符串清洗、类型转换、空值填充、重复值去重。字符串清洗用str.extract把数字抠出来类型转换用pd.to_numeric加errorscoerce强制无效值变 NaN空值填充按列分别处理重复值按“小区户型面积租金”联合去重。import pandas as pd import re df pd.read_csv(house_raw.csv, encodingutf-8) # 租金提取数字按元/月统一 df[rent] df[price].str.extract(r(\d)).astype(float) # 面积提取数字剔除0和异常大值 df[area] df[info].str.extract(r(\d\.?\d*)㎡).astype(float) df df[(df[area] 10) (df[area] 200)] # 户型从3室1厅中拆出室和厅 df[bedrooms] df[info].str.extract(r(\d)室).astype(int) df[livingrooms] df[info].str.extract(r(\d)厅).astype(int) # 经纬度缺失记录按小区名回填示例逻辑 df.loc[df[lng].isna(), lng] df[lng].fillna(df.groupby(community)[lng].transform(mean)) # 去重 df df.drop_duplicates(subset[community, bedrooms, area, rent]) df.to_csv(house_clean.csv, indexFalse, encodingutf-8)这段代码的关键在正则表达式(\d)从“4500元/月”里提取 4500(\d\.?\d*)兼容整数和小数面积(\d)室把“3室1厅”中的 3 取出来。面积过滤区间定在 10 到 200 平方米既能去掉车位、隔断房也能过滤掉录入错误的大面积数据经纬度回填用的是同一小区均值比直接丢弃信息更合理。清洗结果house_clean.csv就是进入 Hadoop 的标准数据集。清洗这一步最容易犯的错是“过度清洗”把异常值全部剔除导致样本量不够后面 Hadoop 分析结果没有统计意义。一般保留 3000 条以上有效记录分布均匀即可不必追求完美。3. Hadoop 伪分布式 Hive 数仓让毕设里的大数据不做摆设3.1 伪分布式搭建单机跑通完整 Hadoop 生态Hadoop 在毕设里最常见的形式是伪分布式部署也就是一台机器上同时跑 NameNode、DataNode、ResourceManager 和 NodeManager。虽然数据量完全不需要分布式但这一步的价值在于答辩时能说清楚 Hadoop 组件如何协作HDFS 的存储逻辑、MapReduce 的调度模型在你手里是真实跑过的。生产级集群要 3 台以上机器毕设一台 8G 内存的笔记本足够。常用版本组合是 Hadoop 3.3.x JDK 8 Hive 3.1.x三者都有版本兼容要求。JDK 装好后在core-site.xml配置 NameNode 地址在hdfs-site.xml配置副本数。注意本地跑直接把副本数设为 1设成 3 虽然不报错但单节点会多做两次无意义的复制。yarn-site.xml需要设置内存分配8G 内存的机器给 YARN 分配 2G 比较稳改大了容易把 NodeManager 拖死。!-- core-site.xml -- configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property /configuration !-- hdfs-site.xml -- configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/usr/local/hadoop/tmp/name/value /property property namedfs.datanode.data.dir/name value/usr/local/hadoop/tmp/data/value /property /configurationfs.defaultFS指定了 HDFS 的入口地址所有客户端通过 9000 端口访问dfs.replication为 1 表示只存一份副本单机环境减少写盘开销。dfs.namenode.name.dir和dfs.datanode.data.dir分别存储元数据和数据块文件这两个路径一定要提前建好否则启动时会在日志里看到FileNotFoundException。启动顺序有讲究先hdfs namenode -format格式化 NameNode然后start-dfs.sh启动 HDFS再start-yarn.sh启动调度框架。每一步用jps命令验证进程是否存活正常应该看到 NameNode、DataNode、ResourceManager、NodeManager 四个进程。启动过程中如果 DataNode 起不来优先检查 logs 目录下的.log文件而不是网上搜配置九成问题是路径权限或端口占用。Hive 装在 HDFS 之上需要先在 HDFS 里创建/tmp和/user/hive/warehouse目录并赋权这是新手最容易漏的一步。3.2 建 Hive 外部表HQL 即学即用数据放入 Hive 有两种常见方式内部表由 Hive 全权管理数据文件适合本地实验外部表通过LOCATION指向 HDFS 现有路径数据删了表还在业务上更安全。毕设推荐外部表理由是你可能需要在 Hadoop 跑通后回改清洗逻辑数据还能复用。建表语句如下CREATE EXTERNAL TABLE IF NOT EXISTS house_rent ( community STRING COMMENT 小区名称, district STRING COMMENT 行政区, bizcircle STRING COMMENT 板块, rent FLOAT COMMENT 月租金, area FLOAT COMMENT 面积, bedrooms INT COMMENT 室, livingrooms INT COMMENT 厅, lng DOUBLE COMMENT 经度, lat DOUBLE COMMENT 纬度 ) COMMENT 租房数据清洗表 ROW FORMAT DELIMITED FIELDS TERMINATED BY , STORED AS TEXTFILE LOCATION /user/hive/warehouse/house_rent;字段类型选型原则是能用数字不用字符串租金用FLOAT而不是STRING因为后面对比均价要直接avg()经纬度用DOUBLE保留精度维度字段大小类型不一致会在 join 时出问题。FIELDS TERMINATED BY ,要与 pandas 输出 CSV 的分隔符完全一致Hive 默认的\001分隔符在从 CSV 导入时要显式更换否则整张表只有一列。数据装载用一条 HQL 搞定LOAD DATA INPATH /input/house_clean.csv INTO TABLE house_rent;INPATH是从 HDFS 路径导入不是本地路径UPLOAD 到 HDFS 用hadoop fs -put house_clean.csv /input/。装载后立刻验证行数和分区粒度SELECT count(*), count(distinct district) FROM house_rent;正常情况能一眼看到记录总数和各区县数量如果没有报错但 count 为 0先回 HDFS 检查文件是否真的存在、大小是否为 0再确认字段分隔符是不是逗号——这两个问题占了 Hive 空表原因的八成。3.3 离线指标计算SQL 替代 MapReduceHive 最有价值的地方在于把 MapReduce 封装成 SQL三个常用指标就能覆盖毕设的核心分析诉求各区均价排行、面积与租金相关性、户型供应占比。这些 SQL 在答辩时即使被追问底层 MapReduce 逻辑也能从容说明。-- 指标1各行政区平均租金排序 SELECT district, ROUND(AVG(rent), 2) AS avg_rent, COUNT(*) AS house_cnt FROM house_rent WHERE rent 0 GROUP BY district ORDER BY avg_rent DESC; -- 指标2按面积段统计平均租金观察梯度 SELECT CASE WHEN area 30 THEN 30平以下 WHEN area 60 THEN 30-60平 WHEN area 90 THEN 60-90平 ELSE 90平以上 END AS area_range, ROUND(AVG(rent), 2) AS avg_rent, COUNT(*) AS cnt FROM house_rent GROUP BY CASE WHEN area 30 THEN 30平以下 WHEN area 60 THEN 30-60平 WHEN area 90 THEN 60-90平 ELSE 90平以上 END;第一个查询按行政区聚合后取租金均值ROUND(..., 2)控制保留两位小数ORDER BY avg_rent DESC让结果直接看出哪个区租金最高COUNT(*)兼职验证样本量防止某个区只有两条数据拉高均价。第二个查询用CASE WHEN分桶注意GROUP BY后面必须重复完整的CASE表达式不能直接写别名这是 Hive 和 MySQL 差异最大的地方——Hive 的 GROUP BY 发生在 SELECT 别名计算之前。house_cnt的作用是过滤极端值样本量小于 5 的分组不进入可视化展示。4. Flask 后端 API把分析结果变成可调用的接口4.1 Hive 结果导出到 MySQLHive 查询结果不能直接被 Flask 查询因为 Flask 应用对用户响应要求毫秒级Hive 的启动开销通常是秒级。常见做法是把离线分析结果导出到 MySQLFlask 作为 Web 层只和 MySQL 通信Hive 负责“算一次存一次”。导出方式有两种数据量小直接hive -e SELECT ... result.csv再导入 MySQL数据量大用 Sqoop。毕设场景用前者更直接。hive -e SELECT district, ROUND(AVG(rent),2), COUNT(*) FROM house_rent WHERE rent 0 GROUP BY district; /tmp/result_district.csv mysql -uroot -p123456 -e LOAD DATA LOCAL INFILE /tmp/result_district.csv INTO TABLE rent_district FIELDS TERMINATED BY \\t (district, avg_rent, house_cnt);这里的关键是分隔符Hive 默认输出用\t分割字段LOAD 语句里也要指定FIELDS TERMINATED BY \t\\t是 shell 转义后的制表符。MySQL 侧的字段顺序要和 CSV 列顺序一致否则数据会错列错行。导入后跑SELECT COUNT(*) FROM rent_district验证条数比预期的区县数多通常是因为 Hive 输出带了表头。4.2 Flask 最小接口设计与 CORS 处理Flask 要做的事情非常克制提供 3 到 5 个 GET 接口每个接口对应一个可视化图表的数据源。一个合理的接口清单是/api/district_avg返回行政区均价柱状图数据/api/area_rent返回面积-租金散点图数据/api/top10返回租金最高的 10 个小区/api/heatmap返回经纬度和租金用于地图气泡/api/community_search?q支持小区关键词搜索。from flask import Flask, jsonify, request import pymysql app Flask(__name__) def get_conn(): return pymysql.connect( hostlocalhost, userroot, password123456, databaserent_house, charsetutf8mb4, cursorclasspymysql.cursors.DictCursor ) app.route(/api/district_avg) def district_avg(): conn get_conn() try: with conn.cursor() as cursor: cursor.execute( SELECT district, avg_rent, house_cnt FROM rent_district WHERE house_cnt 5 ORDER BY avg_rent DESC ) data cursor.fetchall() return jsonify({code: 0, data: data}) except Exception as e: return jsonify({code: 500, msg: str(e)}), 500 finally: conn.close() if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)jsonify会把 DictCursor 返回的字典列表直接序列化为 JSONcode: 0是约定成功标志前端判断res.code 0再渲染数据异常时返回 500 和错误信息方便排查。host0.0.0.0允许局域网访问这样手机或另一台电脑也能打开 Vue 页面请求数据debugTrue在开发阶段有用上线前必须关掉否则代码报错会直接泄露路径信息。finally块里的conn.close()确保每次请求都释放连接Flask 默认单线程模型下不关连接会很快耗尽 MySQL 的最大连接数。from flask_cors import CORS CORS(app)有了这行Flask 接口就允许前端的跨域请求。本地开发时 Vue 和 Flask 通常一个是localhost:8080、一个是localhost:5000浏览器会拦截端口不同的 AJAX 请求CORS(app)解决的是这个问题。不要自己写Access-Control-Allow-Origin响应头flask_cors已经处理了预检请求和各种边界情况。5. Vue 前端可视化ECharts 和地图让数据说话5.1 Vue 环境与项目结构前端部分用 Vue 2 Vue Router ECharts axios 的组合最为顺手。Vue 3 的组合式 API 对新手不友好毕设重点是做出可用的查价和看趋势界面Vue 2 的选项式 API 心智负担小得多。用 npm 创建项目npm install -g vue/cli vue create rent-frontend cd rent-frontend npm install echarts4.9.0 axios vue-router3.5.1ECharts 特意固定 4.x 版本原因是 ECharts 5 的按需引入配置变复杂插件结构也不同直接引用全量包反而简单。vue-router3是对应 Vue 2 的版本号装成 4.x 会直接报路由匹配失败。组件结构建议是views/MapView.vue放地图热力与散点views/TrendView.vue放均价排序柱状图与面积租金散点views/SearchView.vue放小区搜索与详情卡片。路由配置需要处理history模式下的刷新 404 问题。Vue Router 常用createWebHistory()但这种模式刷新页面时后端没有对应路由会返回 404。Flask 侧需要增加兜底路由或者干脆用createWebHashHistory()带#的 hash 模式部署简单不出错。5.2 axios 请求与 ECharts 渲染核心页面的逻辑几乎一样mounted里发请求拿到数据后setOption渲染图表。以地图热力图为例地图用 ECharts 的 scatter 类型加visualMap组件经纬度作为坐标租金作为权重值。template div refchart stylewidth: 100%; height: 600px;/div /template script import echarts from echarts; import axios from axios; export default { name: HeatMap, data() { return { chart: null }; }, mounted() { this.chart echarts.init(this.$refs.chart); this.loadData(); }, methods: { async loadData() { const res await axios.get(/api/heatmap); if (res.data.code 0) { const points res.data.data.map(item ({ value: [item.lng, item.lat, item.rent], })); this.chart.setOption({ tooltip: { trigger: item }, visualMap: { min: 500, max: 15000, dimension: 2 }, series: [{ type: scatter, data: points, symbolSize: 12, itemStyle: { opacity: 0.6 } }] }); } } }, beforeDestroy() { this.chart.dispose(); } }; /scriptvalue数组的前两个元素对应经纬度第三个是租金visualMap的dimension: 2告诉 ECharts 用第三个维度的值映射颜色。symbolSize固定为 12 避免极端值把别的点盖住如果数据量大改成函数形式symbolSize: val val[2] / 500按租金等比缩放会更直观。beforeDestroy里调用dispose()防止组件切换后内存泄漏这是 Vue ECharts 最容易忽略的性能点。5.3 本地反向代理解决跨域开发环境中即使 Flask 已经开了 CORS推荐的做法还是用 Webpack 的 devServer 做代理。理由是对毕设来说最终要部署上线前端和后端在同一个域名下最省事代理让本地环境无限接近生产环境。// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://localhost:5000, changeOrigin: true } } } };前端请求/api/district_avg时devServer 会把请求转发到http://localhost:5000同路径地址。如果后端接口路径带前缀比如/rent/api还需要加pathRewrite: { ^/api: /rent/api }做路径改写。changeOrigin: true必需它会让代理请求头中的 Host 和目标一致后端做任何域名校验时不会误判。6. 联调排错与答辩演示的三个关键技巧联调阶段按“数据 → 接口 → 图表”的顺序排查能省一半时间。先确认 MySQL 表里有数据再确认 Flask 接口浏览器访问有 JSON 返回最后看 Vue 控制台有没有请求报错。一个常见问题是 Vue 请求成功但图空白这时打开浏览器开发者工具看 Network 面板返回的data字段是不是空数组通常从 SQL 条件过滤掉样本量小于 5 的分组就能暴露问题。另一个高频报错是 Flask 返回的数据里avg_rent是 Decimal 类型jsonify无法序列化需要在查询语句里先CAST(avg_rent AS FLOAT)或者写一个自定义 JSON 转换器统一处理。演示顺序建议从“整体看板”切入先打开地图页面展示房源空间分布用户在大区域点击后进入对应行政区的均价柱状图和面积租金散点最后搜索某个小区名看到实时详情。这套交互路径把 Hadoop、Flask、Vue 三个技术点全部串起来评委无论从哪层深挖都有内容。答辩前预演一遍接口容错把租房数据 CSV 里加两行脏数据重新跑清洗到 Hive 的完整链路只操作一遍就记住每个坑的位置。本文还有配套的精品资源点击获取
返回列表