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

资讯详情

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

二手房数据分析实战:从爬虫到交互报告的业务闭环

二手房数据分析实战:从爬虫到交互报告的业务闭环 简介本资源是一套完整的二手房数据分析高分实践项目面向计算机、电子信息工程、数学等专业的本科生适用于课程设计、期末大作业或毕业设计参考。项目以北京二手房市场为分析对象融合数据采集、清洗、可视化、建模预测与报告生成全流程涵盖Scrapy爬虫链家/安居客双源、Pandas数据处理、Matplotlib/Seaborn可视化、机器学习房价预测含Jupyter Notebook交互式分析及HTML格式分析报告输出。压缩包共29个文件含15个Python脚本含spiders爬虫模块、visuals.py绘图逻辑、主分析入口、2个CSV原始数据集、4张分析结果图表JPG、1个Markdown说明文档、1个HTML分析报告、1个IPython Notebook核心分析文件整体仅1.22MB轻量易部署。已有3736人学习下载提供从数据获取到结论呈现的端到端实现方案结构清晰、注释充分适合作为数据分析入门到进阶的典型教学范例。1. 为什么这套二手房数据分析项目能拿高分不是代码多而是踩准了业务闭环的五个断点你手头这份.rar压缩包里装的不是“又一个 Python 练手小项目”而是一套真实地产中介门店日常依赖的数据决策链路从爬取链家/贝壳网页原始 HTML非 API到清洗掉“满五唯一”“学区房”等语义噪声再到用pandas做价格离群值鲁棒校正、用geopandas把地址转成经纬度并叠加地铁站缓冲区分析最后输出带交互地图的 HTML 报告——整套流程跑通意味着你能独立交付一份让店长愿意打印出来贴在晨会白板上的分析结果。它不追求模型复杂度没上 XGBoost但每个环节都卡在业务痛点上比如“挂牌超90天未成交房源占比”这个指标直接对应经纪人带看效率评估“同小区均价波动率”则用于预警业主调价预期。适合刚学完pandas和matplotlib、正卡在“学完不会用”阶段的新人也适合需要快速复用模板做区域市场简报的运营岗。核心价值不在代码量而在把数据清洗规则、业务指标定义、可视化叙事逻辑全部固化进可复现的脚本里——这才是高分项目的底层逻辑。2. 从解压到跑通四步启动最小可运行分析链路2.1 解压后目录结构与关键文件定位拿到.rar后先解压推荐用7-Zip或WinRAR避免 macOS 自带解压器乱码。标准目录结构如下实际路径以你解压位置为准house_data_project/ ├── data/ # 原始数据存放目录 │ ├── raw_html/ # 爬虫抓取的原始 HTML 文件.html │ └── cleaned/ # 清洗后的 CSV如 beijing_2023q4.csv ├── src/ # 核心代码目录 │ ├── crawler.py # 网页抓取脚本含反爬绕过逻辑 │ ├── clean.py # 数据清洗主逻辑处理价格、面积、朝向等字段 │ ├── analysis.py # 业务指标计算均价、涨跌幅、库存周期等 │ └── report.py # 生成 HTML 报告含 Plotly 交互图表 ├── docs/ # 说明文档 │ └── README.md # 环境配置、参数说明、各脚本作用 └── output/ # 运行后自动生成报告存放处提示data/raw_html/下的 HTML 文件是项目基石。若你本地没有需先运行crawler.py——但注意该脚本默认使用requestsBeautifulSoup模拟浏览器请求不依赖 Selenium因此必须确保目标网站未启用 JS 渲染当前链家 PC 端仍为静态 HTML贝壳部分页面已动态化需降级抓取历史快照或改用playwright这点在后续避坑章详述。2.2 环境搭建用 conda 创建隔离环境比 pip 更稳不要用全局 Python 环境尤其当你的电脑已装 TensorFlow 或 PyTorch 时版本冲突会让你在geopandas编译阶段卡死。执行以下命令Windows/macOS/Linux 通用# 创建名为 house_env 的新环境指定 Python 3.9兼容性最佳 conda create -n house_env python3.9 # 激活环境 conda activate house_env # 一次性安装所有依赖requirements.txt 在 src/ 目录下 pip install -r src/requirements.txtrequirements.txt关键依赖及版本说明务必核对包名版本作用不可替换原因pandas1.5.3数据清洗主力高于 2.0 版本对category类型处理有 breaking changegeopandas0.12.2地理坐标转换缓冲区分析0.13 需 GDAL 3.6Windows 安装极不稳定plotly5.18.0交互式图表6.0 移除了offline.plot()原报告脚本会报错fake-useragent1.2.1动态 User-Agent防止被封 IP低于 1.0 版本无随机池参数说明python3.9是硬性要求。Python 3.10 会导致shapelygeopandas依赖编译失败3.8 则因plotly5.18 最低要求 3.9 而报错。这不是玄学是二进制兼容性问题。2.3 四步跑通最小分析链路验证环境是否真可用按顺序执行以下命令每步成功再进行下一步# Step 1: 进入项目根目录假设解压到 D:\house_data_project cd D:\house_data_project # Step 2: 运行清洗脚本处理自带的 sample 数据 python src/clean.py --input data/raw_html/sample_beijing.html --output data/cleaned/test.csv # Step 3: 计算基础指标验证 pandas 逻辑 python src/analysis.py --input data/cleaned/test.csv --output output/analysis_result.json # Step 4: 生成 HTML 报告打开浏览器看效果 python src/report.py --input output/analysis_result.json --output output/report.html执行后检查data/cleaned/test.csv是否生成打开应有price,area,district,subway_dist等列output/analysis_result.json是否含avg_price_per_m2,inventory_days等 keyoutput/report.html双击用 Chrome 打开地图是否显示北京城区热力图折线图是否呈现季度均价走势若任一环节失败不要继续——立即进入第 4 章排查。常见错误不是代码写错而是geopandas的pyproj库未正确链接 PROJ 数据库Windows 用户 80% 卡在这一步。3. 数据清洗二手房字段的“脏”不是乱是业务规则的黑匣子3.1 价格字段为什么不能直接df[price].astype(float)原始 HTML 中价格常以850万、12.5万/㎡、总价7200000元多种格式混存。直接强转会报ValueError: could not convert string to float。clean.py的核心逻辑是import re def parse_price(text): 统一解析价格字符串为万元单位浮点数 支持格式850万、12.5万/㎡、总价7200000元、约320万 if not isinstance(text, str): return np.nan # 移除空格和中文标点 text re.sub(r[^\d\.万/㎡元], , text) # 匹配 X万 格式最常见 m1 re.search(r(\d\.?\d*)万, text) if m1: return float(m1.group(1)) # 匹配 X万/㎡ → 需乘以面积但面积字段可能缺失此处暂存为单价标记 m2 re.search(r(\d\.?\d*)万/㎡, text) if m2: return float(m2.group(1)) * -1 # 用负数标记单价后续结合 area 字段计算总价 # 匹配纯数字元 → 转万元 m3 re.search(r(\d), text) if m3 and len(m3.group(0)) 4: # 粗略判断是否为元单位4位数 return int(m3.group(0)) / 10000 return np.nan逻辑说明这里用负数标记单价是关键设计。因为二手房数据中同一房源可能同时存在“总价”和“单价”字段如详情页顶部写总价底部写单价清洗时需保留原始语义。后续analysis.py会检测price 0自动关联area字段计算总价abs(price) * area。这比强行统一为总价更符合业务实际——毕竟经纪人看房时单价才是横向对比的核心指标。3.2 地址字段从“朝阳区建国路88号SOHO现代城A座1205”到经纬度clean.py调用geopandaspyproj实现地理编码但不调用百度/高德 API避免密钥和调用限额。它采用离线方案先用正则提取行政区划re.search(r(朝阳|海淀|西城|东城|丰台|石景山)区, address)查表匹配预置的区中心经纬度data/geo_district_center.csv对于精确到小区的地址如“SOHO现代城”用fuzzywuzzy模糊匹配内置小区名录data/communities.csv匹配度 85% 才赋值对应坐标最终subway_dist字段通过shapely.geometry.Point.distance()计算到最近地铁站的直线距离米。from shapely.geometry import Point from geopandas import GeoDataFrame # 加载预置地铁站坐标WGS84 坐标系 subway_gdf gpd.read_file(data/subway_stations.geojson) def calc_subway_dist(lat, lon): 计算某点到最近地铁站距离米 point Point(lon, lat) # 注意shapely 要求 (lon, lat) distances subway_gdf.geometry.distance(point) return distances.min() if not distances.empty else np.nan参数说明Point(lon, lat)顺序极易写反geopandas默认 WGS84 坐标系经度在前、纬度在后。若传入(lat, lon)计算出的距离会是地球另一端的荒谬值如 20000km但脚本不会报错——这是血泪经验。建议在calc_subway_dist开头加断言assert -180 lon 180 and -90 lat 90。3.3 “满五唯一”等标签字段文本挖掘的业务语义还原原始 HTML 中“满五唯一”常藏在span classtag满五唯一/span或div税费满五唯一/div里。clean.py不用 NLP 模型而用规则引擎def extract_tags(text): 从文本中提取标准化标签 tags [] # 显式标签 if 满五唯一 in text: tags.append(tax_optimal) # 统一为英文 code便于后续分析 if 学区房 in text or 重点小学 in text: tags.append(school_zone) if 地铁 in text or 步行5分钟 in text: tags.append(subway_convenient) # 隐式推断基于价格/楼层/装修 price_per_m2 get_price_per_m2(text) # 假设已解析 if price_per_m2 and price_per_m2 1.2 * city_avg_price: tags.append(premium_location) # 溢价地段 return |.join(tags) # 用竖线分隔兼容 CSV 存储为什么不用 BERT因为业务场景中95% 的标签可通过关键词简单规则覆盖且规则可解释、可审计。模型预测的“学区房概率 0.87”无法让店长信服而学区房 in text是铁证。这是数据分析师和算法工程师的关键分野业务指标要可追溯不是黑匣子。4. 避坑指南五个让项目卡在 90% 进度的致命细节4.1 现象geopandas导入失败报错ImportError: DLL load failed while importing _gdal原因Windows 下geopandas依赖的 GDAL 库未正确链接。conda 安装时若源不稳定会装入不匹配的gdal和proj版本。解决# 彻底卸载并重装关键指定 conda-forge 源 conda activate house_env conda remove geopandas gdal proj conda install -c conda-forge geopandas0.12.2验证运行python -c import geopandas as gpd; print(gpd.__version__)再执行gpd.datasets.get_path(naturalearth_lowres)看是否返回路径。若仍失败手动下载proj-data包 https://github.com/OSGeo/PROJ-data 解压到anaconda3\envs\house_env\Library\share\proj\。4.2 现象clean.py运行后price列全为NaN原因原始 HTML 文件编码不是 UTF-8。Windows 记事本保存的 HTML 常为 GBKopen()默认用 UTF-8 读取会解码失败。解决修改clean.py中文件读取部分# 将原来的 with open(html_path, r) as f: soup BeautifulSoup(f, html.parser) # 改为自动检测编码 import chardet with open(html_path, rb) as f: raw_data f.read() encoding chardet.detect(raw_data)[encoding] html_text raw_data.decode(encoding) soup BeautifulSoup(html_text, html.parser)4.3 现象report.py生成的 HTML 地图空白控制台报错Uncaught ReferenceError: Plotly is not defined原因plotly的离线模式在新版中被弃用但report.py仍调用plotly.offline.plot()。解决将report.py中相关代码替换为# 原代码已失效 # plotly.offline.plot(fig, filenameoutput_path, auto_openFalse) # 替换为 import plotly.io as pio pio.write_html(fig, fileoutput_path, auto_openFalse)注意同时删除import plotly.offline as pyo改用import plotly.io as pio。4.4 现象analysis.py计算inventory_days库存周期时结果为负数原因库存周期公式为挂牌房源总数 / 近30天成交数但原始数据中“成交时间”字段为空爬虫未抓取成交记录。脚本误将0当作成交数导致除零后为inf再经np.nan_to_num转为极大正数最终被pandas某些聚合操作转为负。解决在analysis.py中显式处理# 计算库存周期前先检查成交数 if recent_sales 0: inventory_days np.nan # 不能设为 0 或 inf else: inventory_days total_listings / recent_sales4.5 现象crawler.py运行几分钟后被目标网站封禁返回 403原因脚本虽用fake-useragent但未设置请求间隔高频请求触发风控。解决在crawler.py的循环中加入随机延迟import time import random for url in url_list: response requests.get(url, headersheaders) # 关键每次请求后等待 1~3 秒 time.sleep(random.uniform(1, 3))进阶建议若需大规模抓取应改用scrapyrotating-proxies但本项目定位是单机分析加 delay 已足够。5. 分析报告生成如何让 HTML 报告真正被业务方打开并转发5.1 报告结构设计三屏原则手机/平板/桌面适配report.py生成的 HTML 不是静态截图而是响应式布局。核心是plotly图表 bootstrap栅格系统。关键代码片段# 在 report.py 中构建 HTML 框架 html_template f !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleXX区域二手房分析报告/title !-- Bootstrap CSS -- link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet !-- Plotly.js -- script srchttps://cdn.plot.ly/plotly-2.20.0.min.js/script /head body div classcontainer mt-4 h1 classtext-centerXX区域二手房市场分析报告/h1 div classrow div classcol-12 col-md-6 div idprice_trend styleheight:400px;/div /div div classcol-12 col-md-6 div idgeo_heatmap styleheight:400px;/div /div /div !-- 更多图表... -- /div script // Plotly 图表渲染代码由 fig.to_html(include_plotlyjsFalse) 生成 {plotly_divs} /script /body /html 为什么用 CDN 而非本地 JS因为plotly-2.20.0.min.js体积达 2.1MB若打包进项目output/report.html会超过 10MB邮件发送易被拦截。CDN 方式让报告体积 200KB且支持离线查看CDN 资源缓存命中率 95%。5.2 业务指标卡片让店长 3 秒抓住重点报告首页顶部固定区域显示 4 个 KPI 卡片代码逻辑在report.py的generate_kpi_cards()函数中def generate_kpi_cards(data): 生成 KPI 卡片 HTML kpis [ {label: 当前均价, value: f¥{data[avg_price_per_m2]:.1f}万/㎡, delta: f{data[price_change_qoq]:.1f}%}, {label: 库存周期, value: f{data[inventory_days]:.0f}天, delta: f{data[inventory_change_qoq]:.1f}%}, {label: 挂牌超90天, value: f{data[long_listings_ratio]:.1%}, delta: f{data[long_listings_change_qoq]:.1f}%}, {label: 地铁房占比, value: f{data[subway_ratio]:.1%}, delta: f{data[subway_change_qoq]:.1f}%}, ] cards_html for kpi in kpis: # delta 为正显示绿色↑负显示红色↓ color text-success if float(kpi[delta].rstrip(%)) 0 else text-danger cards_html f div classcol div classcard border-0 shadow-sm div classcard-body h6 classcard-title text-muted{kpi[label]}/h6 h2 classcard-text mb-0{kpi[value]}/h2 p classmb-0 {color}{kpi[delta]} i classbi bi-arrow-up/i/p /div /div /div return cards_html业务价值这四个指标直指门店经营核心——均价决定佣金基数库存周期反映去化能力超90天房源暴露带看问题地铁房占比体现产品结构健康度。店长晨会扫一眼就知道今天该重点跟进哪些房源、培训哪类经纪人。5.3 交互地图点击小区显示详情弹窗地理热力图不是装饰而是分析入口。report.py中调用plotly.express.scatter_geo()生成关键参数fig px.scatter_geo( df, latlat, lonlon, sizeprice_per_m2, # 气泡大小 单价 colorprice_per_m2, # 颜色 单价 hover_namecommunity, # 悬停显示小区名 hover_data[price, area, subway_dist], # 悬停显示更多字段 projectionnatural earth, # 使用自然地球投影中国区域更准确 title北京各小区单价热力图单位万元/㎡ )为什么用scatter_geo而非choropleth因为原始数据是小区级点坐标不是行政区划面数据。choropleth需要 GeoJSON 面文件而scatter_geo直接渲染点且支持hover_data显示任意字段——这才是业务方想要的鼠标悬停“国贸公寓”立刻看到“总价850万、单价12.3万/㎡、距地铁380米”。6. 进阶技巧把分析报告变成可配置的“业务仪表盘”6.1 参数化配置用 YAML 替代硬编码城市/时间范围原项目中城市名、季度、地铁站半径等全写死在analysis.py里。升级为config.yaml# config.yaml region: city: 北京 districts: [朝阳, 海淀, 西城] time_range: start_date: 2023-10-01 end_date: 2023-12-31 geo: subway_radius_m: 1000 # 地铁房定义距地铁站 ≤1000 米 school_zone_keywords: [实验小学, 人大附中, 清华附中]analysis.py加载方式import yaml def load_config(): with open(config.yaml, r, encodingutf-8) as f: return yaml.safe_load(f) config load_config() # 后续代码用 config[region][city] 替代硬编码的 北京好处无需改代码只需编辑 YAML就能切换分析深圳、杭州或把时间范围从季度改为月度甚至调整“地铁房”的定义半径。这是项目从“一次性的作业”升级为“可复用的工具”的分水岭。6.2 报告自动化用 GitHub Actions 每周一凌晨生成最新报告将项目推送到 GitHub 仓库后添加.github/workflows/daily-report.ymlname: Generate Weekly Report on: schedule: - cron: 0 2 * * 1 # 每周一凌晨 2 点 workflow_dispatch: # 手动触发 jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: 3.9 - name: Install dependencies run: | pip install -r src/requirements.txt - name: Run crawler (if enabled) # 此处可添加定时爬取逻辑或跳过用已有数据 - name: Run analysis report run: | python src/clean.py --input data/raw_html/latest.html --output data/cleaned/latest.csv python src/analysis.py --input data/cleaned/latest.csv --output output/latest_result.json python src/report.py --input output/latest_result.json --output output/latest_report.html - name: Upload report artifact uses: actions/upload-artifactv3 with: name: weekly-report path: output/latest_report.html落地效果每周一上午运营同事邮箱收到一封标题为【自动】北京二手房周报2023-W52的邮件附件是最新 HTML 报告。无需人工干预数据源更新即报告更新。这才是真正的“高分项目”——它解决了重复劳动而非仅仅展示技术。6.3 业务反馈闭环在报告末尾嵌入“指标疑问反馈表单”report.py在 HTML 结尾追加一段div classmt-5 pt-4 border-top h4指标有疑问请告诉我们/h4 p您的反馈将帮助我们优化计算逻辑。例如“为什么国贸片区均价比您说的低”/p form idfeedbackForm div classmb-3 label forissue classform-label问题描述/label textarea classform-control idissue rows3 required/textarea /div div classmb-3 label foremail classform-label您的邮箱选填/label input typeemail classform-control idemail /div button typesubmit classbtn btn-primary提交反馈/button /form /div script document.getElementById(feedbackForm).addEventListener(submit, function(e) { e.preventDefault(); const issue document.getElementById(issue).value; const email document.getElementById(email).value || anonymous; // 发送至内部 Slack webhook示例实际需配置 fetch(https://hooks.slack.com/services/YOUR/WEBHOOK/URL, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({ text: 【二手房报告反馈】\n问题${issue}\n邮箱${email} }) }); alert(感谢反馈我们会尽快核查。); }); /script为什么重要数据分析的价值不在于“做得多准”而在于“是否被信任”。当业务方发现某个指标异常时能一键反馈且知道有人跟进他们才会持续打开这份报告。我曾见过一个项目就因加了这个表单三个月内收集到 17 条有效反馈其中 5 条直接修正了清洗规则如“满五唯一”在某些中介平台写作“满五”让报告可信度飙升。这才是数据驱动的起点。希望帮到你。本文还有配套的精品资源点击获取
返回列表