资讯动态

Spark与爬虫结合的二手房数据清洗、房价预测与大屏可视化实战

发布时间:2026/9/11 19:19:34 来源:尧图企业网站定制
简介基于Spark大数据平台开发的二手房信息爬虫分析与预测系统毕业设计资源面向需要完成大数据实战项目的计算机专业学生及开发者。系统采用Scrapy爬取二手房数据存入MySQL 5.7再由Spark完成统计分析并将结果写入HDFS网站后端使用Flask、前端使用Vue并搭配大屏展示同时结合线性回归算法对房价趋势进行预测覆盖数据采集、存储、分析、可视化与预测全流程。压缩包共397个文件大小约39.71MB包含44个Python代码、34个Vue前端组件、159个SVG图标/图形资源、2个SQL数据库脚本、10个能直接运行的BAT脚本以及MP4操作视频、Markdown说明文档等目录清晰环境配置与启动方式均有注明。已有186人学习下载项目经运行测试可用下载后如遇运行问题可私信远程教学适合作为课程设计或毕业设计的完整参考。1. 毕业设计里的重头戏Spark与爬虫如何凑到一块一个典型的毕设题目写成“基于Spark大数据平台二手房信息爬虫分析预测系统”拆开看其实是四条链路的串联网络爬虫负责把二手房网页上的房源字段抓下来Spark接手做离线清洗和统计分析再基于干净数据训练房价预测模型最后把分析结果通过前端图表砸到大屏上。这套架构本身不新鲜但它是少数能在一个毕设周期内把“数据采集—数据工程—机器学习—可视化交付”整条流水线完整跑通的项目这也是它能反复出现在各类毕业设计题目里的原因。做这个系统之前先要认清它的技术选型边界爬虫部分用 Python 生态Spark 用 PySpark 接入存储落到 HDFS 或本地文件系统可视化的数据接口通过 Spark 分析结果落地成表格或 JSON 喂给前端。这里最容易犯的错是“为了 Spark 而 Spark”——如果数据量只有几万条单机 DataFrame 完全能处理引入 Spark 集群反而让数据倾斜和 shuffle 调优成为新的痛点。一个常见做法是先让爬虫和预测逻辑在单机跑通再用 Spark 做全量重放集群只是放大计算能力不是解决一切问题的银弹。适合读这篇文章的人是打算自己做毕设、或者接手类似“爬虫大数据分析”题目的学生和初级工程师。下面章节按“采集 — 清洗分析 — 建模预测 — 大屏展示”的顺序展开每步都会给出可复现代码和参数说明。2. 二手房爬虫的架构设计并发与反爬的平衡2.1 爬虫技术选型Requests 还是 Scrapy二手房网站的结构差异很大有的数据直接渲染在 HTML 里有的走接口返回 JSON。第一步先分析目标站点的数据来源用浏览器开发者工具查看 Network 面板如果列表页数据出现在XHR请求中直接用 Requests 模拟接口更高效如果数据嵌在 HTML 里则要考虑解析成本Scrapy 的 Selector 在处理这类 DOM 提取时更方便。import requests import json def fetch_lianjia_data(page1): url https://example.com/api/list params {page: page, limit: 30} headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://example.com/ } resp requests.get(url, paramsparams, headersheaders, timeout10) if resp.status_code 200: return resp.json() else: print(f请求失败: {resp.status_code}) return None这段代码的核心是params和headers两个参数params把翻页参数独立出来便于循环调用headers中的 User-Agent 和 Referer 是绕过基础反爬的最小配置。真实项目中这里还应该加一个指数退避重试机制即请求失败后等待 1 秒、2 秒、4 秒再重试而不是立即重发。Requests 适合小规模采集量级在万条以内时足够稳定。如果目标是百万级房源数据需要引入 Scrapy 的并发调度能力。Scrapy 的CONCURRENT_REQUESTS参数控制并发请求数DOWNLOAD_DELAY控制请求间隔。这两个参数直接决定采集速度和被封风险之间的平衡并发高、延迟低采集快但 IP 容易被封反之则慢但稳。常见配置是并发 8延迟 1.5 秒既保证效率又不会触发网站风控。2.2 爬虫并发设计线程、协程还是分布式热词里反复出现“爬虫 并发设计 到底哪个好”这其实是每个做爬虫的人都会卡住的问题。对于二手房信息这种中等规模采集三种方案的选择依据可以这样概括方案适用场景关键实现风险点线程池单机万级数据ThreadPoolExecutorGIL 限制IO 密集时可接受协程单机十万级asyncioaiohttp代码复杂度上升对库依赖多分布式爬虫百万级以上Scrapy Redis 任务队列需要额外维护中间件from concurrent.futures import ThreadPoolExecutor, as_completed def crawl_one_page(page): data fetch_lianjia_data(page) # 这里做字段解析和数据落盘 return len(data) with ThreadPoolExecutor(max_workers4) as executor: futures [executor.submit(crawl_one_page, p) for p in range(1, 101)] for future in as_completed(futures): result future.result() print(f本页采集房源数: {result})max_workers4并不是拍脑袋定的二手房网站的单次请求响应时间一般在 200-500ms4 个线程能让 CPU 和网络等待时间重叠也不会给对方服务器造成过大压力。协程方案虽然性能更好但 Python 的异步爬虫库生态不如 Requests/Scrapy 成熟对毕设而言维护成本偏高。分布式爬虫在数据量和部署复杂度上都超出毕设需求除非题目明确要求“高并发”字样否则不建议为它多花时间。2.3 反爬应对和 IP 代理池二手房网站的常见反爬手段有三种User-Agent 检测、请求频率检测、验证码。前两种通过构造合理的请求头和限速可以解决验证码则需要识别服务或人工介入毕设阶段一般用打码平台或直接绕过——绕过方式是找站点的 APP 接口APP 端的验证码频率通常比 Web 端低。IP 代理池是绕不过去的话题。常见做法是维护一个代理列表每次请求随机取一个 IP失效则剔除并补充。注意代理池只对 IP 维度限流的站点有效如果对方通过设备指纹或账号行为分析限流换 IP 意义不大。proxies [ http://1.2.3.4:8080, http://5.6.7.8:8080, ] def fetch_with_proxy(url, headers): proxy random.choice(proxies) try: resp requests.get(url, headersheaders, proxies{http: proxy, https: proxy}, timeout10) return resp except Exception: proxies.remove(proxy) # 简单失效剔除 return None这段代码里有两个注意点proxies参数要同时指定 http 和 https否则 HTTPS 请求会报代理错误失效剔除要加锁否则多线程下会抛list.remove并发异常。真正生产级的代理池会定期验证代理可用性并计算响应延迟但毕设只要保证不崩即可。采集后的数据格式要保持统一常见的做法是每页抓取结果直接追加为 JSON Lines 格式一行一条房源记录字段包括小区名、户型、面积、朝向、楼层、总价、单价、所在区域和挂牌时间。这样下游 Spark 读取时只需要一次spark.read.json就能加载完毕不需要额外的 ETL 转换。3. Spark 数据清洗与分析DataFrame 的正确打开方式3.1 Spark 集群搭建的最小步骤数据分析需要 Spark 环境本地开发机和毕设演示机配置一般不高的化用伪分布式模式最简单即一台机器同时扮演 Master 和 Worker。由 Spark 官方下载对应 Hadoop 版本的预编译包解压后配置spark-env.sh中的 JVM 参数即可启动。测试环境用local[*]模式就能跑通全流程集群模式只影响最终演示时的吞吐量。启动集群后用 PySpark 创建一个 SparkSession这是所有分析的人口from pyspark.sql import SparkSession spark SparkSession.builder \\ .appName(HousePriceAnalysis) \\ .master(local[4]) \\ .config(spark.sql.shuffle.partitions, 8) \\ .config(spark.executor.memory, 2g) \\ .getOrCreate()这里需要解释两个关键参数spark.sql.shuffle.partitions默认值 200 是针对大规模集群设的本地跑 4 核时调节成 8 能减少小文件碎片spark.executor.memory控制每个 Executor 堆内内存超出物理内存会触发频繁 GC表现就是任务卡住不动。本地开发时这两个参数是比较常见的调试点。3.2 DataFrame 清洗和字段标准化加载原始 JSON 后第一时间要做的工作是类型推断和缺失值处理。爬虫采集到的字段往往全是字符串比如面积字段可能是“89.5平”总价可能是“450万”这些都要转换后才能做后续聚合。from pyspark.sql.functions import col, regexp_replace, to_numeric, when, avg df spark.read.json(/path/to/house_data.jsonl) df_clean df \\ .withColumn(area, regexp_replace(col(area), 平, )) \\ .withColumn(area, to_numeric(col(area))) \\ .withColumn(total_price, regexp_replace(col(total_price), 万, )) \\ .withColumn(total_price, to_numeric(col(total_price))) \\ .withColumn(unit_price, col(total_price) * 10000 / col(area)) \\ .where(col(area).isNotNull() col(unit_price) 0) \\ .dropDuplicates([小区名, 户型, 面积, 总价])逻辑说明第一步去掉单位后缀第二步转数值类型第三步计算单价第四步过滤掉面积为空和单价异常的数据最后按业务唯一键去重。这里的dropDuplicates看的是房源天然维度比distinct()更能防止同一条房源在不同爬取批次中重复入库。Spark 的 DataFrame API 与 pandas 在写法上有相似之处但底层是 lazy 执行以上所有withColumn和where在调用count()或write前不会真正跑数据。调试阶段建议先df_clean.limit(100).show()查看中间结果确认清洗逻辑没有问题后再做全量处理。3.3 区域维度聚合与统计指标计算清洗完数据后常见的分析内容是对各区房源做挂牌均价、总价中位数、面积分布和供需比统计。这里要用到groupBy和聚合函数同时需要区分均值和中位数的适用性——二手房价格存在明显长尾豪宅房源会把均值拉高中位数更能反映普通购房者的感知。from pyspark.sql.functions import count, avg, median, round region_stats df_clean \\ .groupBy(区域) \\ .agg( count(*).alias(房源数), round(avg(unit_price), 0).alias(均价), round(median(total_price), 0).alias(中位数总价) ) \\ .orderBy(col(房源数).desc()) region_stats.show()这里agg里可以混合多个聚合函数Alias 用于重命名结果列。注意median在某些 Spark 版本中需要通过approxQuantile或 percent_rank 实现如果报错可以换成expr(percentile_approx(total_price, 0.5))两者逻辑一致。统计结果直接写回文件或数据库作为大屏展示的数据来源。region_stats.write.mode(overwrite).parquet(/path/to/output/region_stats)输出用 Parquet 格式比 CSV 更适合下游系统原因有三列式存储便于按指标做部分读取自带 schema 不用重复定义列类型压缩率高节省空间。如果后续大屏展示要通过 SQL 查询直接把 Parquet 注册成临时表的方式是region_stats.createOrReplaceTempView(region_stats) spark.sql(SELECT * FROM region_stats WHERE 房源数 100 ORDER BY 均价 DESC LIMIT 10).show()Spark 分析完成后的落盘数据一般不大大屏展示完全可以直接读 CSV 或 Parquet 文件不需要特意引入 Elasticsearch 或 Doris 这类重型存储组件。4. 房价预测模型从特征工程到 MLlib 调参4.1 特征工程的边界与设计预测二手房总价或单价输入特征可以分为三类房源自身属性面积、户型、朝向、所在楼层、小区特征建筑年代、绿化率、物业费、区位特征距离地铁站距离、所属商圈、教育资源等级。爬虫能采集到的一般只有第一类部分小区信息可以从链家小区详情页补采区位特征如果要自己做地理编码工作量会非常大。毕设阶段的特征是并不需要特别完整但需要合理。建议特征集合保持如下范围特征列类型处理方式面积数值直接使用或取对数压缩长尾户型室/厅/卫数值拆分成三个独立数值列朝向类别映射为东/南/西/北/南北等编码所在楼层类别分为低/中/高层建筑年代数值从“1998年建”里提取年份区域类别One-Hot 编码注意稀疏性距离地铁数值从描述文本中抽距离缺失填中位数特征工程的核心原则是宁可少而准不要多而杂。小区名不能直接作为特征因为类别基数过高一个小区可能只有一条样本模型学不到泛化信息反而导致过拟合。区域特征控制在 5-15 个类别即可超过这个数量就要考虑是否换高层级特征。4.2 Spark MLlib 的算法选择与训练流程Spark MLlib 提供的回归算法中随机森林和梯度提升树GBT对表格数据的预测效果较好线性回归适合做基线。本科毕设一般建议先跑线性回归再对比随机森林通过 RMSE 或 MAE 指标选出更优模型。这里逻辑要严格遵循机器学习的标准流程先切训练集和测试集再训练再评估避免用全量数据训练后评估造成数据泄露。from pyspark.ml.feature import VectorAssembler, StringIndexer, OneHotEncoder from pyspark.ml.regression import RandomForestRegressor from pyspark.ml.evaluation import RegressionEvaluator # 特征向量组装 feature_cols [area, bedrooms, living_rooms, building_age, floor_level_index, region_index] assembler VectorAssembler(inputColsfeature_cols, outputColfeatures_vec) # 类别特征编码 region_indexer StringIndexer(inputCol区域, outputColregion_index) floor_indexer StringIndexer(inputCol所在楼层, outputColfloor_level_index) # 索引转独热可选 encoder OneHotEncoder(inputCols[region_index, floor_level_index], outputCols[region_onehot, floor_onehot]) # 随机森林回归器 rf RandomForestRegressor(featuresColfeatures_vec, labelColtotal_price, numTrees100, maxDepth10, seed42)上述代码只完成了特征向量的构建训练时要用Pipeline把多个阶段串起来避免手动逐列处理的顺序错乱。其中StringIndexer有一个坑默认按频率排序新数据中出现未见过的类别时会抛错需要设置setHandleInvalid(keep)或skip来避免预测错误。4.3 评估指标与模型诊断训练完成后用RegressionEvaluator分别算 RMSE 和 MAE。RMSE 对大误差更敏感能反映模型是否在个别异常房源上严重偏离MAE 更直观可以直接说“预测均价平均差 X 万”。如果 RMSE 远大于 MAE说明存在个别预测误差特别大的样本通常来自豪宅或特殊户型。evaluator_rmse RegressionEvaluator(labelColtotal_price, predictionColprediction, metricNamermse) evaluator_mae RegressionEvaluator(labelColtotal_price, predictionColprediction, metricNamemae) rmse evaluator_rmse.evaluate(predictions) mae evaluator_mae.evaluate(predictions) print(fRMSE: {rmse:.2f} 万) print(fMAE: {mae:.2f} 万)模型调参需要控制变量先固定numTrees100在maxDepth为 5、10、15 三档中对比验证集误差选最优后再调节numTrees。不要同时动多个参数否则分不清是哪个参数带来的效果改变。如果分析完特征重要性后发现面积和区域占了 80% 以上权重可以适当删减弱特征降低过拟合风险。5. 大屏展示的数据对接与核心防坑5.1 可视化大屏的技术选型二手房分析预测系统的大屏展示通常分两块静态指标卡片总房源数、均价、最高单价、区域排名和动态图表区域价格柱状图、价格分布直方图、预测价格趋势。前端框架在 ECharts 和 AntV 之间选一个即可ECharts 的文档更全社区样例多适合快速完成毕设演示。大屏数据对接方式有两种一是后端提供 REST 接口返回 JSON 给前端异步加载二是把 Spark 分析结果导出为静态 JSON 文件前端启动时直接 fetch。毕设演示场景下方案二更省事不需要额外写一个 Web 服务也避免了 CORS 跨域配置带来的麻烦。// 前端读取静态 JSON 数据 fetch(/data/region_stats.json) .then(response response.json()) .then(data { initBarChart(data); }); function initBarChart(data) { const chart echarts.init(document.getElementById(bar-chart)); const option { title: { text: 各区二手房均价 }, tooltip: { trigger: axis }, xAxis: { type: category, data: data.map(item item.区域) }, yAxis: { type: value }, series: [{ type: bar, data: data.map(item item.均价), itemStyle: { color: #5470c6 } }] }; chart.setOption(option); }静态 JSON 的关键在于字段命名要和 Spark 落盘时的列名保持一致。Spark 写入 JSON 时会自动生成part-00000-xxx.json这样的分片文件如果数据量小用coalesce(1)合并成单个文件再丢给前端目录会更方便。5.2 大屏参数调整与性能优化大屏调整时最常遇到两个问题图表刷新卡顿和不同分辨率下布局错位。刷新卡顿的原因是每次setOption都重新构建整个图表处理办法是把series数据单独提取出来用setOption({ series: [{ data: newData }] })做局部更新。布局错位则要用rem单位配合屏幕宽度计算基准字体大小而不是写死像素值。提示大屏展示要求的是“看得清、不花哨”不要在一屏里堆超过六个图表。视觉过载会让答辩老师抓不住重点数据指标的选择比图表样式重要得多。5.3 预测结果的展示策略预测模型的结果不只是“某个房子的价格是多少”更有价值的是“价格随面积变化的曲线”。将测试集样本按面积分桶计算每个桶的平均预测价格和真实价格两条线放在同一张折线图里既能直观看到模型拟合度也适合答辩时解释模型效果。实现方式是在完成模型预测后用groupBy按面积段聚合得到预测均价再写入 JSON 供前端读取。from pyspark.sql.functions import floor # 按面积分桶桶宽 10 平米 df_with_pred df_clean.join(predictions.select(原文ID, prediction), on原文ID) \\ .withColumn(面积段, (floor(col(area) / 10) * 10).cast(int)) \\ .groupBy(面积段) \\ .agg(round(avg(prediction), 0).alias(预测均价), round(avg(total_price), 0).alias(真实均价)) \\ .orderBy(面积段) df_with_pred.write.mode(overwrite).json(/path/to/output/price_curve)这里用floor(area / 10) * 10实现的10 平方米分桶方式比较实用面积 45-55 的房源会被分到 50 这一桶桶的数据量相对均匀不会出现大户型样本过少导致曲线尾部剧烈抖动。如果分桶太细比如 5 平米90 平米以上区间可能每个桶只有几条记录平均值不稳定。5.4 集群资源不足时的兜底方案如果你在演示现场出现集群内存不足或节点掉线的情况最快的兜底方案是切换 Spark 运行模式。代码侧不用改只把SparkSession.builder.master(local[4])改为local[2]甚至local[1]减少分配的 Executor 资源虽然计算慢一些但能保证不崩溃。另外一个常见做法是提前把分析结果用 CSV/JSON 缓存一份万一 Spark 环境在答辩现场起不来仍能通过预先生成的数据文件完成展示。5.5 一段前端防白屏技巧大屏页面作为展示入口它的容错很重要。封装一个fetchData函数当 JSON 数据请求失败时用本地内置的 mock 数据兜底渲染这样即使后端数据文件被误删或路径配错演示也不会出现白屏尴尬。这个技巧只需要在前端实现和后端 Spark 没有耦合属于纯前端兜底逻辑但它的实战价值很高——毕设答辩现场的网络环境往往不可控静态文件的读取也可能因为相对路径问题需要调整。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价