Python爬虫实战:廊坊二手房数据采集清洗与可视化分析
简介一份针对廊坊市二手房市场的数据爬取与分析文献以链家网二手房为研究实例系统展示了从爬虫设计、数据采集、清洗转换到可视化分析的全链路项目管理方法完整程度适合作为软件研发人员的技术参考和数据分析初学者的实战教程。该资源内容源于正式期刊压缩包为单个PDF文件容量仅1.3MB包含项目背景、爬虫框架原理说明、核心代码解析、结果分析图表等模块。目前已有199人学习读者可从中掌握网页数据抓取的基本流程、常见解析与提取技巧、数据清洗规则以及如何通过可视化探索变量之间的关系并获得影响房价因素的关键结论。同时也可借助文中的爬虫实现思路和实证分析方法迁移应用到其他城市或相关行业的市场调研中。 最近帮朋友做环京区域市场调研数据源直接选了廊坊市二手房挂牌信息用 Python 跑完了一整套从数据爬取到数据分析的流程。整个项目做下来最大的感受是爬虫只是最前端的一小步真正决定项目价值的是你能不能把抓下来的脏数据洗干净并用合理的维度讲清楚市场结构。这篇就以廊坊市二手房为例把从技术选型、爬虫实现、数据清洗到可视化分析的完整链路写清楚。适合两类人看一类是刚学 Python 数据采集想拿真实数据练手的另一类是关注房产数据想自己搭一套可复用分析流程的读者。1. 为什么拿廊坊二手房当爬虫分析样本1.1 廊坊市场的数据分层优势选择目标城市时我最看重的是数据是否有“层次感”。廊坊市地处北京与天津之间下辖广阳区、安次区以及三河、大厂、香河、固安、永清等多个县市板块不同板块的二手房挂牌价差异非常大。靠近北京的板块因为通勤因素价格明显偏高市区则配套成熟、总价温和远郊板块又有大量低总价房源。这种明显的价格梯度让后续的区域对比分析有很强的可读性不会像某些单核城市那样大家挤在一起分不出差异。头部房产平台对廊坊二手房的信息披露相对完整列表页和详情页基本包含了小区名称、户型、面积、朝向、楼层、总价、单价、挂牌时间等字段。字段越完整数据分析能做的文章就越多。对于爬虫练习来说这也是一个很友好的目标不需要登录就能看到公开挂牌信息页面结构稳定反爬强度适中非常适合作为入门到进阶的过渡项目。1.2 通过这份数据能回答什么问题动手之前我习惯先把问题清单列出来。没有明确问题就开爬最后很容易陷入“数据抓了一堆不知道干嘛”的尴尬。这次我围绕廊坊二手房市场定了五个具体问题各区域板块的挂牌均价到底差多少梯队怎么划分总价段主要集中在哪个区间普通家庭的主力预算范围在哪主流户型结构是什么几室几厅占比最高面积与总价之间是否呈现线性关系单价在不同面积段稳不稳定如果只从数据角度初筛哪些小区存在明显的低价挂牌。这些问题不复杂但每一条都能通过爬下来的数据给出量化答案。等到分析和可视化阶段你会发现有一个明确的问题列表能帮你少走很多弯路也不会在Excel里来回折腾半天还不知道看什么。2. 爬虫方案选型requests能解决的不急着上框架2.1 技术栈requests BeautifulSoup 的适用边界很多人一提到爬虫就想到 Scrapy实际上对于廊坊二手房这个体量requests BeautifulSoup 完全够用。一次全量采集下来单机跑几万条数据没有任何压力Scrapy 的优势在于异步并发、中间件体系和分布式扩展但如果项目复杂度没到那个程度徒增学习成本和调试成本。我的选型逻辑很简单数据量小、页面结构清晰、反爬强度中等那就用轻量方案。requests 负责发 HTTP 请求BeautifulSoup 负责解析 HTMLpandas 负责后续的数据处理和输出。这套组合的优点是每一步都能看得见、摸得着出了问题很好定位。对于第一次做数据采集项目的人我强烈建议先用这种方式把完整链路跑通再考虑要不要上框架。2.2 环境准备Anaconda虚拟环境和VSCode解释器开发环境上我推荐用 Anaconda 创建独立虚拟环境避免和系统 Python 或其他项目产生包冲突。特别是你电脑里可能同时有多个 Python 版本直接装包容易把环境搞乱。用 conda 建环境之后所有依赖都隔离在项目内部换机器或换项目都不用担心。conda create -n lfang python3.10 -y conda activate lfang pip install requests beautifulsoup4 pandas matplotlib lxml这里有一个特别容易踩的坑很多人用 VSCode 写代码但装包用的是终端里的 conda 环境运行代码时解释器却还指向系统 Python结果 import pandas 直接报错。配好环境后一定记得在 VSCode 右下角把 Python 解释器切到对应的 conda 环境。我因为这个原因浪费过不少时间后来习惯在跑代码前先执行python -c import pandas; print(pandas.__version__)确认环境。3. 页面解析与采集细节从URL规律到字段落盘3.1 先摸清URL结构和页面字段写爬虫之前不要急着打开编辑器先在浏览器里手动翻几页列表页观察 URL 的参数规律。廊坊二手房列表页通常通过 URL 中的区域代码和分页参数控制展示内容我这次以某头部房源平台的公开频道为例列表页结构大致是https://xxx.com/lfang/lf/pg2/其中lf表示廊坊城市代码pg2表示第 3 页索引从 0 开始。不同区域板块对应不同的 URL 前缀所以爬取时可以直接按照区域逐个采集顺便把区域字段记录下来后续做区域对比会非常方便。页面解析的关键是先用开发者工具定位房源条目所在的 DOM 节点。列表页里每条房源通常有统一的 CSS 类名比如.sellListContent下的.info节点。用 BeautifulSoup 的选择器可以准确提取标题、链接、总价、单价和基础信息。注意选择器一定要以页面实际结构为准这个模拟示例只是帮你理解思路。import time import random import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, } session requests.Session() def fetch_page(url, max_retry3): for i in range(max_retry): try: resp session.get(url, headersHEADERS, timeout10) if resp.status_code 200: return resp.text except requests.RequestException as e: print(请求异常, url, e) time.sleep(random.uniform(2, 4)) return None3.2 Session请求头与请求参数模拟很多平台会校验请求头里的 User-Agent、Referer、Accept-Language 等字段如果某个字段缺失或不符合浏览器特征轻则返回异常页面重则直接拒绝访问。我的做法是尽量把请求头补全同时用requests.Session()保持连接状态。Session 在连续请求多个页面时可以复用底层连接效率更高也能避免某些基于会话的校验频繁触发。请求头伪装不是让你去伪造身份绕过权限而是让请求看起来像正常浏览器访问。这个边界要清楚只采集公开可见的信息不碰需要登录才能看的内容不尝试绕过验证码或风控接口。很多网站都有 robots 协议和服务条款爬虫用于个人学习研究没问题但不要大规模高频率抓取更不要拿数据做商业用途。3.3 频率控制、异常重试与字段落盘反爬最有效的防御其实不是高深技术而是“别太贪婪”。列表页连续访问间隔我设定在 1 到 3 秒随机波动每抓 50 页休息 10 秒。这种力度下几千条数据大概十几分钟就能跑完对目标服务器也足够友好。解析列表页时需要提取的字段包括标题、链接、户型、面积、朝向、装修、楼层、总价、单价。有些平台详情页里还有小区名称和建筑年代但列表页已经能满足大部分分析需求。为了控制请求量我这次只抓列表页详情页字段通过列表页已有的信息补全不额外发请求。采集到的数据我直接用 pandas 存成 CSVimport pandas as pd data [] # 省略解析循环提取结果逐条 append 到 data df pd.DataFrame(data) df.to_csv(langfang_housing.csv, indexFalse, encodingutf-8-sig)用utf-8-sig编码很重要不然用 Excel 打开 CSV 时中文会乱码。这个细节我是在第一次导出后踩了坑才记住的。4. 数据清洗把“能看但很乱”的表格变成分析素材4.1 去重、去噪音先把垃圾记录清出去爬下来的数据往往比想象中脏。最常见的几个问题重复记录、非住宅房源混入、关键字段为空。去重可以直接用过房源链接作为唯一标识如果同一条房源在多个分页里重复出现用链接去重最可靠。df df.drop_duplicates(subset链接, keepfirst)非住宅房源主要表现是标题里带“车位”“地下室”“仓库”等关键词。这些记录对住宅价格分析是干扰项应该直接过滤掉。判断时可以用字符串匹配noise_keywords [车位, 地下室, 仓库, 商铺] mask df[标题].str.contains(|.join(noise_keywords)) df df[~mask].copy()空值处理上面积或总价缺失的记录我直接删除。因为后续回归和均价计算都依赖这两个核心字段少量缺失删掉影响不大强行填充反而引入噪声。4.2 字段标准化从“3室1厅”到可计算的数值页面上的文本格式不适合直接计算。“90.5平米”“135万”“13500元/平”“3室1厅1卫”这些字符串必须先转成数值或拆分字段。我的处理思路是用正则表达式提取核心数字再做类型转换。df[面积] df[面积].str.replace(平米, ).astype(float) df[总价] df[总价].str.replace(万, ).astype(float) df[单价] df[单价].str.replace(元/平, ).str.replace(,, ).astype(float) room_pattern df[户型].str.extract(r(\d)室(\d)厅(\d)卫) df[室] pd.to_numeric(room_pattern[0]) df[厅] pd.to_numeric(room_pattern[1]) df[卫] pd.to_numeric(room_pattern[2])字符串转数值时最烦人的是隐藏字符比如空格、中文逗号、全角数字。先str.strip()再转换。如果转换时报错可以用pd.to_numeric(..., errorscoerce)把无法转换的变成 NaN后面统一处理。我用errorscoerce之后发现数据里确实混着一些格式奇葩的记录这些就是需要丢弃或手工修正的对象。4.3 异常值处理别让极端样本带偏均值异常值的判断不能拍脑袋要结合业务常识。廊坊市场普通住宅面积很少低于 20 平米也很少超过 400 平米总价低于 20 万的个人住宅基本不存在单价如果突然出现每平米几千甚至几百多半是数据解析错误或者房源类型特殊。我的做法是先画分布图再结合描述性统计设定过滤阈值print(df[[面积, 总价, 单价]].describe()) df df[(df[面积] 20) (df[面积] 400)] df df[(df[总价] 20) (df[总价] 2000)] df df[(df[单价] 3000) (df[单价] 50000)]这里要提醒一句异常值不一定是错误也可能是真实存在的极端房源。比如别墅大面积高总价或者远郊小户型低总价。处理时不要一刀切太狠建议先看有多少比例的数据被过滤如果超过 2%就要检查是不是阈值设得有问题。5. 分析可视化从区域价差到面积总价关系5.1 区域挂牌均价对比第一张图我先看区域价格梯度。用 pandas 的 groupby 按区域聚合算平均单价再排序画柱状图。这一步能直接回答“廊坊哪个板块挂牌价最高”的问题也是对数据清洗效果的一次直观验证。area_price df.groupby(区域)[单价].mean().sort_values(ascendingFalse) area_price.plot.bar(figsize(10, 5), title廊坊各区域二手房挂牌均价)从我采样的数据看靠近北京通勤圈的几个板块挂牌单价明显高于市区和远郊板块形成了清晰的三个梯队第一梯队是与北京接壤的热门板块第二梯队是广阳、安次等主城区第三梯队是外围县市。如果只看整体平均价这个结构会被掩盖所以区域维度一定要拆开看。5.2 总价分箱与户型结构总价分布用pd.cut做分箱统计观察房源集中在哪些价格带。比如 100 万以下、100 到 150 万、150 到 200 万、200 到 300 万、300 万以上分五档统计数量占比。这样的分析比单纯看平均总价更有现实意义因为平均数容易被极端值拉高而分箱能看出大部分房源的预算集中区间。户型结构直接统计“室”字段的分布。从数据来看两室和三室是市场绝对主力一室小户型在市区比较常见四室以上的房源明显减少。结合总价分箱可以进一步交叉分析不同预算区间的主流户型是什么。这一步用 pandas 的crosstab就能完成。5.3 面积与总价的回归拟合最后我想验证一个直觉面积越大总价越高但斜率在不同区域是否一致。先用散点图看总体关系再拟合一条线性回归线。import numpy as np k, b np.polyfit(df[面积], df[总价], 1) df[预测总价] k * df[面积] b corr df[面积].corr(df[总价]) print(f斜率: {k:.2f}, 截距: {b:.2f}, 相关系数: {corr:.3f})整体相关系数并不像想象中那么接近 1原因在于不同板块的单不一样。市区和热门板块单价高同样的面积总价更高远郊板块单价低大面积房源总价也不一定高。把多个市场混在一起拟合回归线会被“掰弯”。所以更合理的做法是分区域做回归分别看单价水平而不是堆在一起得出一个没有业务含义的总体斜率。这也是我这次分析中最重要的一个结论数据不拆分到合理粒度得到的“规律”可能是假象。6. 爬虫合规与数据口径容易被忽略但决定结论可信度6.1 合规红线公开数据、低频率、不商用爬虫本身没有原罪但使用方式有边界。这次项目我只抓取公开列表页的公开信息不涉及登录态绕过不读取非公开接口不尝试破解验证码或字体加密。请求频率保持克制不给目标服务器造成压力。数据用途限定在个人学习研究和市场趋势观察不做商业倒卖。如果你想长期跟踪某个城市的房价变化最好的方式不是一次把数据抓爆而是每天定时采集少量数据形成自己的历史数据库。低频、长期、可持续的采集方式对服务器更友好得到的时间序列数据也更有价值。6.2 挂牌价与成交价的偏差做房产数据分析最容易忽略的一点是挂牌价不等于成交价。平台展示的是业主期望价格实际成交时通常还有议价空间。所以这次分析得出的所有结论只能说明“挂牌市场”的供给结构和业主预期不能直接等同于真实成交行情。另外挂牌数据本身也有样本偏差。一些成交火热的房源可能挂牌几天就下架如果你在某一天做快照采集看到的是当天的存量房源而不是完整流量。所以爬虫分析适合回答结构性问题比如区域供给分布、户型占比、价格区间不太适合回答“某个小区真实成交价多少”这种颗粒度很细的问题。如果要做成交价分析需要追踪更长周期或者对接有成交记录的数据源。6.3 后续扩展时间序列、GIS和成交对比这个项目完全可以继续往深做。最简单的扩展是定时任务每天跑一遍爬虫把每天的挂牌量、平均单价、新增房源数记录下来形成时间序列观察市场热度变化。再进一步可以把经纬度信息补充进来用地理可视化工具把房源标注在地图上直观展示不同板块的均价热力分布。如果还能拿到真实成交记录就能对比挂牌价和成交价的折价率那将是更有商业价值的分析方向。整套流程跑完我的体会是爬虫只是数据链路里最前端的环节真正决定分析质量的永远是数据清洗和口径统一。工具用 requests 还是 Scrapy、可视化用 Matplotlib 还是 ECharts都没有那么重要重要的是你对数据背后的业务逻辑有没有清晰判断。如果能先把一个城市、一套流程完整跑通再复制到其他城市就只是换参数的问题了。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →