author: 专注Python实战,分享爬虫与数据分析干货
title: Python爬虫实战㉙|综合实战4,房产数据采集与区域价值评估
update: 2026-04-26
tags: Python,爬虫实战,房产,数据分析,区域评估,价格预测,热力图,可视化

作者:专注Python实战,分享爬虫与数据分析干货
更新时间:2026年4月
适合人群:已学完全部基础、想做完整项目的开发者


前言:买房/租房前,先分析数据

在哪买房性价比最高?哪个区域涨幅最大?周边配套怎样?
爬虫采集房源 → 多维度评分 → 区域价值排行,用数据说话。


一、项目目标

1. 爬取房源数据(价格、面积、位置、配套等)
2. 数据清洗与特征工程
3. 区域价值评分模型
4. 可视化:价格热力图、区域排行、配套分析
5. 生成区域价值评估报告

二、数据爬取

2.1 房源爬虫

import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import random
import re

class HouseCrawler:
    """房源数据爬虫"""

    def __init__(self):
        self.session = requests.Session()
        self.data = []

    def crawl_district(self, city_code, district, max_pages=5):
        """爬取某区域房源"""
        for page in range(1, max_pages + 1):
            url = f"https://example-house.com/{city_code}/{district}/pg{page}/"
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0"
            }

            try:
                resp = self.session.get(url, headers=headers, timeout=15)
                resp.raise_for_status()
                resp.encoding = "utf-8"
                soup = BeautifulSoup(resp.text, "html.parser")

                items = soup.select(".house-item")
                for item in items:
                    house = {
                        "标题": self._safe_text(item.select_one(".title")),
                        "小区": self._safe_text(item.select_one(".community")),
                        "区域": district,
                        "总价_万": self._parse_price(self._safe_text(item.select_one(".total-price"))),
                        "单价_元每平": self._parse_unit_price(self._safe_text(item.select_one(".unit-price"))),
                        "面积_平": self._parse_area(self._safe_text(item.select_one(".area"))),
                        "户型": self._safe_text(item.select_one(".layout")),
                        "朝向": self._safe_text(item.select_one(".orientation")),
                        "楼层": self._safe_text(item.select_one(".floor")),
                        "装修": self._safe_text(item.select_one(".decoration")),
                        "建筑年代": self._parse_year(self._safe_text(item.select_one(".build-year"))),
                        "标签": self._safe_text(item.select_one(".tags")),
                    }
                    self.data.append(house)

            except Exception as e:
                print(f"  爬取失败({district} p{page}): {e}")

            time.sleep(random.uniform(2, 5))

    def _safe_text(self, element):
        return element.get_text(strip=True) if element else ""

    def _parse_price(self, text):
        match = re.search(r"([\d.]+)", text)
        return float(match.group(1)) if match else None

    def _parse_unit_price(self, text):
        match = re.search(r"([\d,]+)", text)
        if match:
            return int(match.group(1).replace(",", ""))
        return None

    def _parse_area(self, text):
        match = re.search(r"([\d.]+)平", text)
        return float(match.group(1)) if match else None

    def _parse_year(self, text):
        match = re.search(r"(\d{4})", text)
        return int(match.group(1)) if match else None

    def to_dataframe(self):
        df = pd.DataFrame(self.data)
        df = df.drop_duplicates(subset=["标题", "小区"]).reset_index(drop=True)
        return df

三、数据清洗与特征工程

import pandas as pd
import numpy as np

def clean_house_data(df):
    """房源数据清洗"""

    # 1. 去除缺失关键字的行
    df = df.dropna(subset=["总价_万", "面积_平", "区域"]).copy()

    # 2. 异常值处理
    # 面积 < 10 或 > 500 的剔除
    df = df[(df["面积_平"] >= 10) & (df["面积_平"] <= 500)]
    # 总价 < 10万 或 > 5000万 的剔除
    df = df[(df["总价_万"] >= 10) & (df["总价_万"] <= 5000)]
    # 单价异常
    df = df[(df["单价_元每平"] >= 1000) & (df["单价_元每平"] <= 200000)]

    # 3. 计算衍生特征
    df["单价_元每平"] = df["单价_元每平"].fillna(df["总价_万"] * 10000 / df["面积_平"])

    # 户型提取(几室几厅)
    df["室"] = df["户型"].str.extract(r"(\d+)室").astype(float)
    df["厅"] = df["户型"].str.extract(r"(\d+)厅").astype(float)

    # 房龄
    current_year = 2026
    df["房龄"] = current_year - df["建筑年代"]
    df["房龄"] = df["房龄"].clip(0, 50)

    # 单价分档
    df["价格档"] = pd.cut(df["单价_元每平"],
                        bins=[0, 20000, 40000, 60000, 100000, 200000],
                        labels=["2万以下", "2-4万", "4-6万", "6-10万", "10万以上"])

    df = df.reset_index(drop=True)
    return df

四、区域价值评分

class DistrictScorer:
    """区域价值评分模型"""

    def __init__(self, df):
        self.df = df

    def score_all(self):
        """综合评分"""
        district_stats = self.df.groupby("区域").agg(
            均价=("单价_元每平", "mean"),
            中位价=("单价_元每平", "median"),
            房源数=("标题", "count"),
            平均面积=("面积_平", "mean"),
            平均房龄=("房龄", "mean"),
            总价均值=("总价_万", "mean"),
        ).reset_index()

        # 标准化评分(0-100)
        for col in ["均价", "房源数"]:
            district_stats[f"{col}_分"] = (
                (district_stats[col] - district_stats[col].min())
                / (district_stats[col].max() - district_stats[col].min())
                * 100
            ).round(1)

        # 综合评分 = 价格权重*0.6 + 活跃度*0.4
        district_stats["综合评分"] = (
            district_stats["均价_分"] * 0.6 +
            district_stats["房源数_分"] * 0.4
        ).round(1)

        district_stats = district_stats.sort_values("综合评分", ascending=False)
        return district_stats

五、可视化

import matplotlib.pyplot as plt
import seaborn as sns

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

def plot_district_ranking(scores):
    """区域价值排行"""
    top = scores.head(15)

    fig, ax = plt.subplots(figsize=(10, 7))
    bars = ax.barh(top["区域"][::-1], top["综合评分"][::-1],
                   color=plt.cm.RdYlGn(np.linspace(0.3, 0.9, len(top))))

    for bar, val in zip(bars, top["综合评分"][::-1]):
        ax.text(bar.get_width() + 1, bar.get_y() + bar.get_height()/2,
                f"{val:.1f}", va="center", fontsize=11)

    ax.set_title("区域综合价值评分排行", fontsize=16, fontweight="bold")
    ax.set_xlabel("综合评分", fontsize=12)
    plt.tight_layout()
    plt.savefig("district_ranking.png", dpi=150, bbox_inches="tight")
    plt.show()


def plot_price_distribution(df):
    """价格分布对比"""
    fig, axes = plt.subplots(1, 2, figsize=(14, 6))

    # 单价分布
    top_districts = df["区域"].value_counts().head(6).index
    for district in top_districts:
        subset = df[df["区域"] == district]["单价_元每平"]
        axes[0].hist(subset, bins=30, alpha=0.5, label=district)
    axes[0].set_title("各区域单价分布")
    axes[0].legend()

    # 箱线图
    top_df = df[df["区域"].isin(top_districts)]
    sns.boxplot(data=top_df, x="区域", y="单价_元每平", ax=axes[1])
    axes[1].set_title("各区域单价箱线图")
    axes[1].set_xticklabels(axes[1].get_xticklabels(), rotation=30)

    plt.tight_layout()
    plt.savefig("price_distribution.png", dpi=150, bbox_inches="tight")
    plt.show()


def plot_area_vs_price(df):
    """面积-价格散点图"""
    plt.figure(figsize=(10, 6))
    for district in df["区域"].value_counts().head(5).index:
        subset = df[df["区域"] == district]
        plt.scatter(subset["面积_平"], subset["总价_万"],
                   alpha=0.4, s=20, label=district)

    plt.xlabel("面积(㎡)", fontsize=12)
    plt.ylabel("总价(万)", fontsize=12)
    plt.title("面积 vs 总价(按区域着色)", fontsize=16, fontweight="bold")
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.tight_layout()
    plt.savefig("area_vs_price.png", dpi=150, bbox_inches="tight")
    plt.show()

六、生成评估报告

def generate_report(df, scores):
    """区域价值评估报告"""
    report = f"""
    ╔══════════════════════════════════════╗
    ║    房产区域价值评估报告              ║
    ╚══════════════════════════════════════╝

    📊 市场概况
    ─────────────────────
    房源总量: {len(df)}
    涉及区域: {df['区域'].nunique()}
    整体均价: ¥{df['单价_元每平'].mean():,.0f}/㎡
    中位价: ¥{df['单价_元每平'].median():,.0f}/㎡
    价格区间: ¥{df['单价_元每平'].min():,.0f} - ¥{df['单价_元每平'].max():,.0f}/㎡

    🏆 区域价值TOP5
    ─────────────────────
    """

    for i, row in scores.head(5).iterrows():
        report += (
            f"  {row['区域']}: 评分{row['综合评分']:.1f} "
            f"(均价¥{row['均价']:,.0f}/㎡, {int(row['房源数'])}套)\n"
        )

    report += """
    📈 建议
    ─────────────────────
    - 关注评分高且房源活跃的区域
    - 对比同区域不同户型的性价比
    - 注意房龄对价格的影响
    """

    return report

print(generate_report(df, scores))

七、知识卡

模块 说明
HouseCrawler 房源数据爬虫
clean_house_data() 数据清洗+特征工程
DistrictScorer 区域价值评分
plot_district_ranking 区域排行图
plot_price_distribution 价格分布图
pd.cut() 价格分档

八、课后作业

必做题:

  1. 完成房源数据爬虫
  2. 实现数据清洗和特征工程
  3. 制作区域价值评分

选做题:

  1. 用线性回归预测房价
  2. 生成HTML评估报告

有问题欢迎评论区留言,大家一起讨论!


标签:Python | 爬虫实战 | 房产 | 数据分析 | 区域评估 | 可视化 | 评分模型

更多推荐