2026/9/2 9:37:51

Python爬虫+MongoDB+Folium:链家房源数据采集分析与房价热力图实战

Python爬虫+MongoDB+Folium:链家房源数据采集分析与房价热力图实战 简介本资源是一套面向数据分析初学者与Python爬虫实践者的房地产大数据项目实战方案聚焦武汉链家真实房源数据采集与可视化分析场景。项目完整实现从网页爬取729条房源、MongoDB存储、多维度统计房源类型占比、区域房价水平到热力图/柱状图/饼图等交互式可视化展示的全流程可直接用于课程设计、毕业设计或行业调研参考。压缩包共18个文件含3个核心Python脚本爬取、可视化、数据导出、4张分析结果图热力图、柱形图、饼图、MongoDB数据截图、1份README说明文档、1份附赠资源文档及配套配置文件如fake_useragent.json总大小1.49MB结构清晰、模块解耦便于理解各环节技术逻辑与调试要点。目前已有72人学习下载提供开箱即用的数据采集逻辑、MongoDB连接范例、Pandas清洗模板及MatplotlibHTML热力图实现代码助读者快速掌握爬虫—数据库—分析—可视化的全栈数据处理能力。1. 项目概述与核心价值最近在做一个挺有意思的数据分析项目核心目标是把武汉链家网上的房源信息给“搬”下来然后看看这些数据背后到底藏着什么门道。你可能也好奇一个城市里哪个区域的房子最贵哪种户型的房源最多房价和地理位置到底有多大关系这些问题光靠感觉可不行得用数据说话。这个项目就是干这个的用Python爬虫技术自动抓取了武汉地区729个房源的真实数据存到MongoDB数据库里然后进行了一系列的分析和可视化比如看看房源类型的分布比例分析一下房价的整体水平最后还用热力图把房价和地理位置的关系直观地展示出来。整个过程从数据采集、清洗、存储到分析和可视化算是一个比较完整的数据分析小闭环对于想入门数据分析或者爬虫的朋友来说是个挺不错的练手项目能接触到从技术到业务的全流程。2. 整体技术架构与设计思路2.1 为什么选择这个技术栈做这个项目技术选型上我主要考虑了三点高效获取数据、灵活存储数据、以及便捷地分析和展示数据。对应的就选定了Python爬虫 MongoDB 可视化库这个组合。首先Python爬虫是数据来源的基石。Python的requests、BeautifulSoup、lxml这些库生态成熟写起爬虫来效率很高。链家这类房产网站的结构相对规整虽然有一定反爬措施但通过合理的请求头设置、IP代理池对于大规模抓取必要和速度控制稳定获取几百上千条数据是完全可行的。这里我们没有选择Scrapy这样的重型框架是因为项目规模不大用requests解析库的组合更轻量、更可控。其次MongoDB作为数据存储。为什么不选MySQL因为房源数据是半结构化的每条房源信息包含的字段可能不完全一致比如有的有“装修情况”有的没有而且后续的分析可能涉及嵌套查询如按区域统计各种户型。MongoDB的文档模型BSON格式非常适合存储这种JSON-like的数据插入和查询都非常灵活。特别是做聚合分析时它的聚合管道功能非常强大后面我们会详细讲到。最后分析与可视化部分。数据分析用了pandas它是Python数据处理的“瑞士军刀”。可视化则用了matplotlib做基础图表pyecharts或folium来生成交互性更强的热力图。选择folium是因为它能轻松集成Leaflet地图将地理坐标经纬度映射为热力图直观展示房价的空间分布效果非常棒。2.2 核心流程拆解整个系统的运行流程可以概括为以下四个核心阶段它们环环相扣数据采集层通过Python爬虫程序模拟浏览器访问链家武汉站遍历列表页解析详情页提取房源标题、总价、单价、面积、户型、楼层、朝向、小区名称、行政区、街道、甚至经纬度等关键信息。数据存储层将爬取到的每一条房源信息整理成一个JSON文档直接存入MongoDB数据库。一个文档对应一套房源。数据处理与分析层从MongoDB中读取数据使用pandas进行数据清洗处理缺失值、异常值、格式统一然后进行具体的分析计算如各类户型占比、各行政区均价对比等。数据可视化层将分析结果用图表呈现。使用柱状图、饼图展示占比和对比使用folium根据房源的经纬度和单价/总价生成武汉市的房价热力图。这个流程的设计保证了从数据生产到数据消费的完整性和可复现性。每一个环节的输出都是下一个环节的输入。3. 核心实现细节与实操要点3.1 爬虫策略与反爬应对链家网对爬虫有一定的防护直接猛抓很容易被屏蔽IP。在实际操作中我采用了以下策略来保证爬虫的稳定和礼貌请求头Headers伪装这是最基本的。必须设置User-Agent为一个真实的浏览器标识并带上Referer等常见头部信息。headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://wh.lianjia.com/ }请求频率控制在循环抓取页面时每请求一次后用time.sleep()随机休眠1-3秒。这是最重要的道德和技术策略能极大降低被封风险。import time import random time.sleep(random.uniform(1, 3)) # 随机休眠模拟人工操作代理IP池备用方案对于本项目729条的数据量在控制好频率的前提下通常不需要代理IP。但如果需要大规模抓取构建一个可靠的代理IP池是必须的。可以从一些付费或免费的代理IP网站获取IP并在请求失败时自动切换。数据解析链家页面结构清晰使用BeautifulSoup或lxml配合CSS选择器可以精准定位元素。关键是要找到包含房源列表的容器div以及每个房源条目div的稳定类名或结构。这些选择器需要定期检查因为网站可能会改版。注意务必遵守网站的robots.txt协议并评估数据抓取的目的和规模仅限于个人学习与研究使用。避免对目标网站服务器造成过大压力。3.2 MongoDB数据模型设计在MongoDB中我们以“集合”Collection类似于SQL中的表来存储房源。每条文档结构设计如下这直接决定了后续分析的便利性{ “_id”: ObjectId(“自动生成”), “title”: “武汉锦绣人家 2室1厅 南向”, “total_price”: 185, // 总价单位万元 “unit_price”: 23456, // 单价单位元/平方米 “area”: 78.9, // 面积单位平方米 “layout”: “2室1厅1卫”, // 户型 “floor”: “中层(共18层)”, // 楼层信息 “orientation”: “南”, // 朝向 “district”: “江岸区”, // 行政区 “street”: “后湖大道”, // 街道/板块 “community”: “锦绣人家”, // 小区名 “longitude”: 114.285678, // 经度 “latitude”: 30.623456, // 纬度 “url”: “https://wh.lianjia.com/ershoufang/101012345678.html”, // 详情页链接 “crawl_time”: “2023-10-27 14:30:00” // 爬取时间 }设计心得_id是MongoDB自动生成的主键无需我们操心。将floor字段存为字符串而非拆分是因为原始信息如此且后续分析“高层”、“中层”等概念时用字符串处理更简单。经纬度是生成热力图的关键链家详情页的房源地图里通常有坐标可以通过解析页面JavaScript数据或地图API请求来获取。这是本项目可视化部分的“灵魂”数据。添加crawl_time字段非常重要可以记录数据快照的时间点便于后续追踪数据变化或进行时序分析。3.3 数据清洗与预处理从网上爬下来的数据不可能完全干净。在进行分析前必须进行清洗。主要使用pandas的DataFrame来完成缺失值处理检查关键字段如total_price,unit_price,area是否有缺失。对于单价有时可以通过总价和面积计算得出。对于无法填补的少量缺失可以考虑删除该条记录或标记为未知。异常值处理检查单价或总价是否存在明显不合理的数据如单价低于5000元或高于10万元。这可能是爬虫解析错误或特殊房源如车位。可以通过描述性统计df[‘unit_price’].describe()查看分布并用分位数如1%和99%进行截断或过滤。格式统一area字段可能带有“平米”字样需要提取纯数字。floor字段需要统一表述。layout字段可以拆分成“室”、“厅”、“卫”三个独立的数值型字段便于后续做交叉分析。类型转换确保价格、面积等字段是数值型float或int而不是字符串。import pandas as pd # 假设df是从MongoDB读取的DataFrame # 1. 面积字段提取数字 df[‘area’] df[‘area’].str.extract(‘(\d\.?\d*)’).astype(float) # 2. 拆分户型 df[[‘rooms’, ‘halls’, ‘bathrooms’]] df[‘layout’].str.extract(‘(\d)室(\d)厅(\d)卫’).astype(float) # 3. 处理单价缺失用总价/面积计算 df[‘unit_price’].fillna(df[‘total_price’] * 10000 / df[‘area’], inplaceTrue) # 4. 过滤异常单价 price_low, price_high df[‘unit_price’].quantile([0.01, 0.99]) df df[(df[‘unit_price’] price_low) (df[‘unit_price’] price_high)]清洗后的干净数据才是可靠分析的基础。4. 数据分析与可视化实现详解4.1 房源类型占比分析我们首先想了解市场上哪种户型是主流。这里的“类型”可以指“户型”如2室1厅也可以指“房屋类型”如普通住宅、公寓根据爬取到的字段决定。这里以户型为例。分析思路对layout字段进行频数统计然后计算百分比。使用pandas的value_counts()函数非常简单。layout_distribution df[‘layout’].value_counts().head(10) # 取前10最常见的户型 layout_percentage (layout_distribution / df.shape[0] * 100).round(2) print(“户型分布前10:”) for layout, count in layout_distribution.items(): print(f”{layout}: {count}套 ({layout_percentage[layout]}%)“)可视化用饼图或柱状图展示。饼图适合展示占比柱状图更适合对比数量。import matplotlib.pyplot as plt plt.figure(figsize(10, 6)) layout_distribution.plot(kind‘bar’) plt.title(‘武汉链家房源户型分布Top 10’) plt.xlabel(‘户型’) plt.ylabel(‘数量套’) plt.xticks(rotation45) # 旋转x轴标签 plt.tight_layout() plt.show()通过这个分析你可能发现“2室1厅1卫”和“3室2厅1卫”是绝对的主力户型这反映了市场的主流需求。4.2 房价水平分析房价分析可以从多个维度切入整体描述性统计计算全市房源的总价、单价的平均值、中位数、标准差、最值等。中位数比平均值更能抵抗极端值的影响反映“典型”房价。price_summary df[[‘total_price’, ‘unit_price’]].describe() print(price_summary)行政区划维度分析各个行政区江岸区、江汉区、武昌区等的平均单价和总价并进行排序找出房价高地。district_price df.groupby(‘district’)[‘unit_price’].agg([‘mean’, ‘median’, ‘count’]).round(2).sort_values(by‘mean’, ascendingFalse) print(district_price)这个结果可以用分组柱状图每个行政区一个柱子柱子分平均单价和总价来可视化非常直观。户型-价格关系分析不同户型如2室 vs 3室的平均单价是否有差异。这里可能需要将户型简化为“室数”再进行分组分析。df[‘room_count’] df[‘rooms’] # 假设之前已拆分出rooms字段 price_by_rooms df.groupby(‘room_count’)[‘unit_price’].mean().sort_index() price_by_rooms.plot(kind‘line’, marker‘o’) # 用折线图看趋势 plt.title(‘不同房间数均价对比’) plt.xlabel(‘房间数室’) plt.ylabel(‘平均单价元/平米’) plt.grid(True) plt.show()4.3 热力图可视化展示这是本项目最出彩的部分将抽象的房价数据映射到具体的地理空间上。我们使用folium库。原理folium是Python封装Leaflet.js的地图库。热力图图层HeatMap需要一组经纬度坐标和对应的权重值。在这里每个房源的(latitude, longitude)就是坐标权重weight可以是unit_price单价或total_price总价。权重值越高在地图上的“热度”颜色就越暖如红色。实现步骤准备数据从清洗后的DataFrame中提取[纬度 经度 权重]的列表。权重通常需要归一化到0-1之间或者直接使用原始值folium会内部处理。创建基础地图以武汉市的平均坐标为中心。添加热力图图层。保存为HTML文件可以在浏览器中交互查看。import folium from folium.plugins import HeatMap # 1. 准备数据这里用单价作为权重 heat_data [[row[‘latitude’], row[‘longitude’], row[‘unit_price’]] for index, row in df.iterrows()] # 2. 创建地图设定武汉的大致中心坐标 wh_center [30.59, 114.31] # 武汉大致经纬度中心 m folium.Map(locationwh_center, zoom_start11, tiles‘OpenStreetMap’) # 3. 添加热力图 # max_val参数可以设定权重最大值影响颜色映射。这里取单价数据的95分位数避免极端值影响视觉效果。 max_val df[‘unit_price’].quantile(0.95) HeatMap(heat_data, min_opacity0.2, max_valmax_val, radius15, blur10, gradient{0.4: ‘blue’, 0.65: ‘lime’, 1: ‘red’}).add_to(m) # 4. 保存地图 m.save(‘wuhan_house_price_heatmap.html’)实操心得radius和blur参数控制热力点的扩散范围和模糊程度需要根据地图缩放级别和数据密度调整。gradient参数可以自定义颜色渐变让热力图更符合你的审美。生成的HTML文件是独立的包含了所有JavaScript和CSS分享给任何人他们用浏览器打开就能看到交互式地图可以缩放、拖动体验非常好。通过热力图你能一眼看出武汉房价的核心区域如武昌中南路、汉口建设大道沿线呈现深红色以及价格洼地城市边缘呈现蓝色。这种直观性是数字表格无法比拟的。5. 项目部署、优化与问题排查5.1 从脚本到系统简单部署思路虽然本项目是一个数据分析脚本集合但可以稍作封装使其更工程化。模块化将代码拆分成独立模块如spider.py爬虫、db_handler.py数据库操作、analyzer.py数据分析、visualizer.py可视化。通过一个主程序main.py来调度。配置文件将数据库连接字符串、爬虫请求头、目标URL列表、文件保存路径等配置信息写入一个config.ini或settings.py文件便于管理和修改。日志记录使用Python的logging模块记录程序运行状态、错误信息这对于排查爬虫中断等问题至关重要。定时任务如果希望定期更新数据可以使用系统的crontabLinux或Task SchedulerWindows来定时运行主爬虫脚本。5.2 性能与扩展性优化建议爬虫异步化如果数据量增大到数万条同步的requeststime.sleep模式会非常慢。可以考虑使用aiohttpasyncio实现异步爬虫能成倍提升抓取效率。但异步编程复杂度更高且需更小心地控制并发避免被封。MongoDB索引优化如果数据量很大查询变慢需要在经常查询的字段上建立索引。例如在district、unit_price字段上建立索引可以极大加速按行政区筛选、按价格排序的操作。# 在Python的pymongo中创建索引 db.collection.create_index([(‘district’, pymongo.ASCENDING)]) db.collection.create_index([(‘unit_price’, pymongo.DESCENDING)])增量爬取本项目是“全量”爬取。更高级的做法是“增量”爬取只抓取新增或变动的房源。这需要设计机制来识别新数据例如比较房源唯一ID或最后更新时间。链家房源有唯一的house_code可以利用这个字段。5.3 常见问题与排查实录在开发过程中我遇到了几个典型问题这里分享排查思路爬虫突然获取不到数据返回空列表或错误页面检查点1请求头。网站可能升级了反爬策略检查User-Agent是否有效可能需要更新或添加更多头部信息如Accept-Language、Cookie谨慎使用注意有效期。检查点2IP被封。暂停程序换一个网络环境如手机热点测试单个请求是否成功。如果成功说明原IP被暂时封锁。需要增加请求间隔或引入代理IP池。检查点3页面结构变化。用浏览器开发者工具重新检查目标元素的CSS选择器或XPath路径是否仍然正确。这是最常见的原因需要更新解析代码。MongoDB连接失败或插入数据报错错误信息pymongo.errors.ServerSelectionTimeoutError排查首先确认MongoDB服务是否启动sudo systemctl status mongod。其次检查连接字符串中的IP、端口、用户名密码是否正确。如果是远程连接确保服务器防火墙开放了27017端口。热力图不显示或显示异常现象打开HTML文件地图是白的或者热力点位置完全不对。排查步骤 a.检查坐标数据确认latitude和longitude字段的值是否在合理范围内武汉纬度约30.5经度约114.3。一个常见错误是经纬度顺序弄反Leaflet期望[lat, lon]。 b.检查权重数据确认权重列如单价没有NaN或无限大的值这些值会导致热力图渲染失败。在生成heat_data前先做清洗df df.dropna(subset[‘latitude’, ‘longitude’, ‘unit_price’])。 c.查看浏览器控制台按F12打开开发者工具查看Console是否有JavaScript报错。这能提供更具体的错误信息。数据分析结果与常识不符现象计算出的平均单价高得离谱或低得惊人。排查回到数据清洗步骤。极有可能是异常值没有处理干净。重新检查数据清洗代码中的过滤条件如分位数过滤并查看df.describe()的输出确认最大值和最小值是否合理。也可能是爬虫解析时价格单位万/元或面积单位平米/亩弄错了需要检查解析逻辑。这个项目虽然不大但涵盖了数据获取、存储、处理、分析和可视化的完整链路。做完之后不仅对武汉的二手房市场有了一个数据层面的认识更重要的是把Python数据分析的常用工具和流程实实在在地跑通了一遍。对于初学者我建议可以尝试更换目标城市或者增加分析维度比如结合小区的建筑年代、地铁距离等让分析更有深度。数据处理中清洗和校验环节花的时间往往比想象中多但这是保证结论可信的基石千万不能马虎。本文还有配套的精品资源点击获取