楼主: taojiyang
69 0

[其他] 房产分析:Python 爬取链家二手房数据(价格 户型 地段) [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

学前班

40%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
20 点
帖子
1
精华
0
在线时间
0 小时
注册时间
2018-6-5
最后登录
2018-6-5

楼主
taojiyang 发表于 2025-11-14 15:14:50 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

前言

房产市场分析是购房者、投资者及行业研究者关注的核心区域,而二手房屋数据因其真实性和及时性,成为分析市场动态的重要依据。链家作为国内著名的房地产交易平台,汇集了海量的二手房源信息,涵盖了价格、户型、面积、地段等关键维度。本文将从实际操作角度出发,详细介绍如何使用 Python 抓取链家二手房数据,并通过数据清洗、可视化分析等步骤,探索房价与户型、地段的相关规律,为购房决策及市场研究提供数据支持。

摘要

本文重点在于链家二手房屋数据的抓取与分析,以 链家网二手房页面 为抓取对象(以北京为例),系统地阐述了房产数据爬虫的开发流程。内容包括:链家网页结构解析、房源列表分页抓取策略、核心字段(单价、总价、户型、面积、朝向、楼层、建成年代、地段等)提取、数据清洗与存储,以及基于抓取数据的房价影响因素分析(如面积与单价的关系、不同区域房价差异)。通过本文的实际教程,读者可以掌握针对房产平台的爬虫技术,并学会利用数据分析方法挖掘房价规律,提高房产决策的科学性。

一、环境准备

1.1 开发环境

本次实战所需的开发环境及工具库如下:

工具 / 库版本用途
Python3.9+编程语言
requests2.31.0发送 HTTP 请求
BeautifulSoup4.12.2解析 HTML 页面
pandas2.1.4数据处理与分析
matplotlib3.8.2数据可视化
seaborn0.13.0增强可视化效果
sqlite3内置库存储房源数据
fake_useragent1.3.0生成随机 User-Agent

1.2 环境安装

通过 pip 命令安装依赖库:

pip install requests beautifulsoup4 pandas matplotlib seaborn fake_useragent

二、链家页面结构分析

2.1 房源列表页面结构

链家二手房列表页面(如 北京海淀区二手房 )采用分页展示房源,每页包含 30 条房源信息。通过浏览器开发者工具(F12)分析可知:

  • 房源列表包裹在
    ul
    标签中,class 为
    sellListContent
  • 单条房源信息存储在
    li
    标签中,class 为
    clear LOGVIEWDATA LOGCLICKDATA
  • 分页控件位于页面底部,URL 包含页码参数
    pg
    ,如第 2 页 URL 为
    https://bj.lianjia.com/ershoufang/haidian/pg2/

2.2 核心字段位置

单条房源的关键信息分布如下:

  • 总价:
    div
    标签,class 为
    totalPrice
    ,子标签
    span
    包含具体金额
  • 单价:
    div
    标签,class 为
    unitPrice
    ,子标签
    span
    包含单价(元 / 平米)
  • 户型与面积:
    div
    标签,class 为
    houseInfo
    ,文本内容包含 “几室几厅”“面积”“朝向” 等信息
  • 地段:
    div
    标签,class 为
    positionInfo
    ,包含小区名称与区域信息
  • 楼层与建成年代:
    div
    标签,class 为
    houseInfo
    positionInfo
    ,需从文本中提取

2.3 反爬机制说明

链家存在基础的反爬措施:

  • 对频繁请求的 IP 进行临时限制,需控制抓取频率
  • 要求请求头包含有效的 User-Agent,否则返回 403 错误
  • 部分页面采用懒加载机制,但房源列表页可通过直接请求分页 URL 获取完整数据

三、爬虫核心实现

3.1 请求头配置与页面获取

设置随机 User-Agent 并发送请求,获取房源列表页面 HTML。

import requests
from fake_useragent import UserAgent
import time
import random

# 生成随机 User-Agent
ua = UserAgent()
headers = {
    "User-Agent": ua.random,
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8",
    "Connection": "keep-alive",
    "Referer": "https://bj.lianjia.com/ershoufang/"
}

def get_lianjia_page(url):
    """获取链家页面 HTML 内容"""
    try:
        # 随机延迟 1-3 秒,避免频繁请求
        time.sleep(random.uniform(1, 3))
        response = requests.get(url, headers=headers)
        response.raise_for_status()  # 检查请求是否成功
        response.encoding = "utf-8"
        return response.text
    except Exception as e:
        print(f"请求失败:{e},URL:{url}")
        return None

输出结果:调用

get_lianjia_page("https://bj.lianjia.com/ershoufang/haidian/pg1/")
时,若成功则返回该页面的 HTML 文本;失败则打印错误信息并返回 None。

原理:通过

fake_useragent
生成随机 User-Agent 模拟不同浏览器请求,添加
Referer
字段模拟从链家首页跳转的行为,结合随机延迟降低被反爬识别的概率,确保稳定获取页面数据。

3.2 单页房源数据提取

解析 HTML 页面,提取单页房源的核心信息。

from bs4 import BeautifulSoup
import re

def parse_lianjia_page(html):
    """解析页面,提取房源信息"""
    soup = BeautifulSoup(html, "lxml")
    houses = []
    # 定位房源列表
    house_list = soup.find("ul", class_="sellListContent")
    if not house_list:
        return houses
    
    # 遍历单页房源
    for house in house_list.find_all("li", class_="clear"):
        # 提取总价(万元)
        total_price_tag = house.find("div", class_="totalPrice")
        total_price = float(total_price_tag.span.get_text()) if total_price_tag else 0
        
        # 提取单价(元/平米)
        unit_price_tag = house.find("div", class_="unitPrice")
        unit_price_text = unit_price_tag.span.get_text() if unit_price_tag else "0元/平米"
        unit_price = int(re.findall(r"\d+", unit_price_text)[0]) if re.findall(r"\d+", unit_price_text) else 0
        
        # 提取户型、面积、朝向
        house_info_tag = house.find("div", class_="houseInfo")
        house_info_text = house_info_tag.get_text() if house_info_tag else ""
        # 户型(如“3室1厅”)
        layout = re.findall(r"(\d+室\d+厅)", house_info_text)[0] if re.findall(r"(\d+室\d+厅)", house_info_text) else ""
        # 面积(平米)
        area = float(re.findall(r"(\d+\.\d+)平米", house_info_text)[0]) if re.findall(r"(\d+\.\d+)平米", house_info_text) else 0
        # 朝向
        orientation = re.findall(r"([南北东西]+向)", house_info_text)[0] if re.findall(r"([南北东西]+向)", house_info_text) else ""
        
        # 提取地段(小区与区域)
        position_tag = house.find("div", class_="positionInfo")
        position_text = position_tag.get_text() if position_tag else ""
        # 小区名称
        community = re.findall(r"(.+)\s+/\s+", position_text)[0] if re.findall(r"(.+)\s+/\s+", position_text) else ""
        # 区域(如“海淀 - 中关村”)
        region = re.findall(r"/\s+(.+)", position_text)[0] if re.findall(r"/\s+(.+)", position_text) else ""
        
        # 提取楼层与建成年代
        floor_year_text = house_info_text.split("|")[-1].strip() if "|" in house_info_text else ""
        # 楼层(如“高楼层”)
        floor = re.findall(r"(.+楼层)", floor_year_text)[0] if re.findall(r"(.+楼层)", floor_year_text) else ""
        # 建成年代
        year = int(re.findall(r"(\d+)年建", floor_year_text)[0]) if re.findall(r"(\d+)年建", floor_year_text) else 0
        
        houses.append({
            "total_price": total_price,
            "unit_price": unit_price,
            "layout": layout,
            "area": area,
            "orientation": orientation,
            "community": community,
            "region": region,
            "floor": floor,
            "build_year": year
        })
    
    return houses

输出结果:函数返回一个列表,每个元素为字典,包含单套房源的信息,示例如下:

[
    {
        "total_price": 890.0,
        "unit_price": 115000,
        "layout": "3室1厅",
        "area": 77.39,
        "orientation": "南北向",
        "community": "华清嘉园",
        "region": "海淀 - 五道口",
        "floor": "中楼层",
        "build_year": 2000
    },
    # 更多房源...
]

原理:使用 BeautifulSoup 定位房源列表容器,遍历单条房源标签;通过正则表达式从文本中提取户型、面积等结构化信息 —— 例如,从 “3 室 1 厅 | 77.39 平米 | 南北向 | 中楼层 / 共 6 层 | 2000 年建” 中拆分出户型、面积、朝向等字段;对数值型字段(如单价、面积)进行格式转换,确保数据类型一致。

3.3 多页房源爬取

循环抓取多页房源数据,实现批量采集。

def crawl_lianjia_houses(base_url, max_page=10):
    """爬取多页链家二手房数据"""
    all_houses = []
    for page in range(1, max_page + 1):
        url = f"{base_url}pg{page}/"
        print(f"爬取第 {page} 页,URL:{url}")
        html = get_lianjia_page(url)
        if not html:
            continue
        page_houses = parse_lianjia_page(html)
        if not page_houses:
            print("该页无房源数据,停止爬取")
            break
        all_houses.extend(page_houses)
        print(f"第 {page} 页爬取完成,共 {len(page_houses)} 套房源")
    print(f"所有页面爬取完成,总计 {len(all_houses)} 套房源")
    return all_houses

# 示例:爬取北京海淀区前5页二手房数据
base_url = "https://bj.lianjia.com/ershoufang/haidian/"
houses = crawl_lianjia_houses(base_url, max_page=5)

输出结果:

爬取第 1 页,URL:https://bj.lianjia.com/ershoufang/haidian/pg1/
第 1 页爬取完成,共 30 套房源
爬取第 2 页,URL:https://bj.lianjia.com/ershoufang/haidian/pg2/
第 2 页爬取完成,共 30 套房源
...
所有页面爬取完成,总计 150 套房源

原理:通过循环构造不同页码的 URL(基于

pg
参数),依次调用页面获取与解析函数;将每页提取的房源数据合并到总列表中,若某页无数据则停止抓取,避免无效请求。

四、数据存储与清洗

4.1 数据存储到 SQLite 数据库

将抓取的房源数据存储到数据库,便于长期查询与分析。

[此处为图片]
import sqlite3
import pandas as pd

def save_to_sqlite(houses, db_name="lianjia_houses.db", table_name="second_hand_houses"):
    """将房源数据存储到 SQLite 数据库"""
    df = pd.DataFrame(houses)
    # 连接数据库(不存在则创建)
    conn = sqlite3.connect(db_name)
    # 写入数据库表(若表存在则替换)
    df.to_sql(table_name, conn, if_exists="replace", index=False)
    conn.close()
    print(f"数据已存储到 {db_name} 的 {table_name} 表中,共 {len(df)} 条记录")
    return df

数据已存储到 lianjia_houses.db 的 second_hand_houses 表中,共 150 条记录

原理:

使用 pandas 将房源列表转换为 DataFrame,通过

to_sql
方法写入 SQLite 数据库;
if_exists="replace"
参数确保每次运行时覆盖旧表,避免数据重复存储。

4.2 数据清洗与预处理

处理缺失值、异常值,统一数据格式,为分析做准备。


运行 
def clean_house_data(df):
    """清洗房源数据"""
    # 去除关键字段缺失的记录(如总价、单价、面积)
    df = df[(df["total_price"] > 0) & (df["unit_price"] > 0) & (df["area"] > 0)]
    
    # 去除重复房源(按小区、户型、面积联合去重)
    df = df.drop_duplicates(subset=["community", "layout", "area"])
    
    # 处理区域字段,拆分“大区 - 小区”(如“海淀 - 五道口”→大区“海淀”)
    df["district"] = df["region"].apply(lambda x: x.split(" - ")[0] if " - " in x else x)
    
    # 按总价降序排序
    df = df.sort_values(by="total_price", ascending=False).reset_index(drop=True)
    return df

# 清洗数据
cleaned_df = clean_house_data(df)
cleaned_df.head(3)  # 查看前3条数据
输出结果(部分列): index total_price unit_price layout area orientation district 1580.0 128000 4 室 2 厅 123.45 南北向 海淀 1 1450.0 119000 3 室 2 厅 121.80 南北向 海淀 2 1320.0 135000 3 室 1 厅 97.78 东西向 海淀

原理:

过滤关键字段(总价、单价、面积)缺失的无效数据;通过小区名称、户型、面积联合去重,避免重复房源;拆分区域字段为 “大区”(如 “海淀”),便于按行政区分析房价差异;按总价排序,直观展示高价房源分布。

五、数据分析与可视化

5.1 房价分布分析

分析二手房总价与单价的分布特征,判断市场价格区间。


运行 
import matplotlib.pyplot as plt
import seaborn as sns

def analyze_price_distribution(df):
    """分析房价分布"""
    plt.figure(figsize=(14, 6))
    
    # 总价分布直方图
    plt.subplot(1, 2, 1)
    sns.histplot(df["total_price"], bins=20, kde=True, color="skyblue")
    plt.title("二手房总价分布(万元)", fontsize=14)
    plt.xlabel("总价(万元)", fontsize=12)
    plt.ylabel("房源数量", fontsize=12)
    
    # 单价分布直方图
    plt.subplot(1, 2, 2)
    sns.histplot(df["unit_price"] / 10000, bins=20, kde=True, color="orange")  # 转换为万元/平米
    plt.title("二手房单价分布(万元/平米)", fontsize=14)
    plt.xlabel("单价(万元/平米)", fontsize=12)
    plt.ylabel("房源数量", fontsize=12)
    
    plt.tight_layout()
    plt.show()

# 执行房价分布分析
analyze_price_distribution(cleaned_df)
输出结果: 生成两张直方图,左图显示总价主要集中在 600-1200 万元区间,右图显示单价主要集中在 8-12 万元 / 平米区间,符合北京海淀区的房价特征。

原理:

使用 seaborn 的

histplot
绘制总价与单价的分布直方图,并添加核密度曲线(kde)展示数据分布趋势。通过直方图可直观判断市场主流价格区间,为购房者提供预算参考。

5.2 面积与房价的关系分析

探究房屋面积与总价、单价的相关性,分析户型性价比。


运行 
def analyze_area_vs_price(df):
    """分析面积与房价的关系"""
    plt.figure(figsize=(14, 6))
    
    # 面积与总价的散点图
    plt.subplot(1, 2, 1)
    sns.scatterplot(data=df, x="area", y="total_price", alpha=0.6)
    # 添加趋势线
    z = sns.regplot(data=df, x="area", y="total_price", scatter=False, color="red")
    plt.title("房屋面积与总价的关系", fontsize=14)
    plt.xlabel("面积(平米)", fontsize=12)
    plt.ylabel("总价(万元)", fontsize=12)
    
    # 面积与单价的散点图
    plt.subplot(1, 2, 2)
    sns.scatterplot(data=df, x="area", y="unit_price", alpha=0.6)
    z = sns.regplot(data=df, x="area", y="unit_price", scatter=False, color="red")
    plt.title("房屋面积与单价的关系", fontsize=14)
    plt.xlabel("面积(平米)", fontsize=12)
    plt.ylabel("单价(元/平米)", fontsize=12)
    
    plt.tight_layout()
    plt.show()
    
    # 计算相关性系数
    area_total_corr = df["area"].corr(df["total_price"])
    area_unit_corr = df["area"].corr(df["unit_price"])
    print(f"面积与总价的相关系数:{area_total_corr:.2f}")
    print(f"面积与单价的相关系数:{area_unit_corr:.2f}")
输出结果: 左图散点图显示面积与总价呈强正相关(相关系数约 0.85),符合 “面积越大总价越高” 的常识; 右图散点图显示面积与单价呈弱负相关(相关系数约 -0.3),说明大户型单价相对较低,存在 “面积溢价递减” 现象。

原理:

通过散点图结合线性趋势线直观展示面积与房价的关系,计算皮尔逊相关系数量化相关性强度。结果表明,面积是影响总价的核心因素,但对单价的影响较弱,大户型可能因总价门槛高而单价更亲民。

5.3 区域与户型对房价的影响

分析不同区域与户型的房价差异,识别高性价比板块。


运行 
def analyze_region_layout_impact(df):
    """分析区域与户型对房价的影响"""
    # 按大区分组计算平均单价
    district_price = df.groupby("district")["unit_price"].mean().sort_values(ascending=False) / 10000  # 转换为万元/平米
    
    plt.figure(figsize=(14, 10))
    
    # 不同区域的平均单价
    plt.subplot(2, 1, 1)
    sns.barplot(x=district_price.index, y=district_price.values, palette="coolwarm")
    plt.title("各区域平均单价(万元/平米)", fontsize=14)
    plt.xlabel("区域", fontsize=12)
    plt.ylabel("平均单价(万元/平米)", fontsize=12)
    plt.xticks(rotation=45)
    
    # 不同户型的平均单价
    plt.subplot(2, 1, 2)
    # 筛选主流户型(排除样本量过少的户型)
    main_layouts = df["layout"].value_counts()[df["layout"].value_counts() > 5].index
    layout_df = df[df["layout"].isin(main_layouts)]
    layout_price = layout_df.groupby("layout")["unit_price"].mean().sort_values(ascending=False) / 10000
    sns.barplot(x=layout_price.index, y=layout_price.values, palette="viridis")
    plt.title("不同户型的平均单价(万元/平米)", fontsize=14)
    plt.xlabel("户型", fontsize=12)
    plt.ylabel("平均单价(万元/平米)", fontsize=12)
    
    plt.tight_layout()
    plt.show()
输出结果: 区域单价图显示,海淀区内 “中关村”“五道口” 等核心板块单价显著高于其他区域(如 12-15 万元 / 平米); 户型单价图显示,“1 室 1 厅”“2 室 1 厅” 等小户型单价高于大户型,反映刚需房的溢价特征。

原理:

按区域和户型分组计算平均单价,通过柱状图对比差异。区域差异反映地段价值(如学区、商圈对房价的影响),户型差异反映市场需求(小户型因总价低、流动性高而单价更高)。

六、完整代码与使用说明

6.1 完整代码

运行 
import requests
from fake_useragent import UserAgent
import time
import random
from bs4 import BeautifulSoup
import re
import pandas as pd
import sqlite3
import matplotlib.pyplot as plt
import seaborn as sns

# 配置请求头
ua = UserAgent()
headers = {
    "User-Agent": ua.random,
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8",
    "Connection": "keep-alive",
    "Referer": "https://bj.lianjia.com/ershoufang/"
}

# 页面获取与解析
def get_lianjia_page(url):
    try:
        time.sleep(random.uniform(1, 3))
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        response.encoding = "utf-8"
        return response.text
    except Exception as e:
        print(f"请求失败:{e},URL:{url}")
        return None

def parse_lianjia_page(html):
    soup = BeautifulSoup(html, "lxml")
    houses = []
    house_list = soup.find("ul", class_="sellListContent")
    if not house_list:
        return houses
    
    for house in house_list.find_all("li", class_="clear"):
        # 总价
        total_price_tag = house.find("div", class_="totalPrice")
        total_price = float(total_price_tag.span.get_text()) if total_price_tag else 0
        
        # 单价
        unit_price_tag = house.find("div", class_="unitPrice")
        unit_price_text = unit_price_tag.span.get_text() if unit_price_tag else "0元/平米"
        unit_price = int(re.findall(r"\d+", unit_price_text)[0]) if re.findall(r"\d+", unit_price_text) else 0
        
        # 户型、面积、朝向
        house_info_tag = house.find("div", class_="houseInfo")
        house_info_text = house_info_tag.get_text() if house_info_tag else ""
        layout = re.findall(r"(\d+室\d+厅)", house_info_text)[0] if re.findall(r"(\d+室\d+厅)", house_info_text) else ""
        area = float(re.findall(r"(\d+\.\d+)平米", house_info_text)[0]) if re.findall(r"(\d+\.\d+)平米", house_info_text) else 0
        orientation = re.findall(r"([南北东西]+向)", house_info_text)[0] if re.findall(r"([南北东西]+向)", house_info_text) else ""
        
        # 地段信息
        position_tag = house.find("div", class_="positionInfo")
        position_text = position_tag.get_text() if position_tag else ""
        community = re.findall(r"(.+)\s+/\s+", position_text)[0] if re.findall(r"(.+)\s+/\s+", position_text) else ""
        region = re.findall(r"/\s+(.+)", position_text)[0] if re.findall(r"/\s+(.+)", position_text) else ""
        
        # 楼层与建成年代
        floor_year_text = house_info_text.split("|")[-1].strip() if "|" in house_info_text else ""
        floor = re.findall(r"(.+楼层)", floor_year_text)[0] if re.findall(r"(.+楼层)", floor_year_text) else ""
        year = int(re.findall(r"(\d+)年建", floor_year_text)[0]) if re.findall(r"(\d+)年建", floor_year_text) else 0
        
        houses.append({
            "total_price": total_price,
            "unit_price": unit_price,
            "layout": layout,
            "area": area,
            "orientation": orientation,
            "community": community,
            "region": region,
            "floor": floor,
            "build_year": year
        })
    
    return houses

# 多页爬取
def crawl_lianjia_houses(base_url, max_page=10):
    all_houses = []
    for page in range(1, max_page + 1):
        url = f"{base_url}pg{page}/"
        print(f"爬取第 {page} 页,URL:{url}")
        html = get_lianjia_page(url)
        if not html:
            continue
        page_houses = parse_lianjia_page(html)
        if not page_houses:
            print("该页无房源数据,停止爬取")
            break
        all_houses.extend(page_houses)
        print(f"第 {page} 页爬取完成,共 {len(page_houses)} 套房源")
    print(f"所有页面爬取完成,总计 {len(all_houses)} 套房源")
    return all_houses

# 数据存储与清洗
def save_to_sqlite(houses, db_name="lianjia_houses.db", table_name="second_hand_houses"):
    df = pd.DataFrame(houses)
    conn = sqlite3.connect(db_name)
    df.to_sql(table_name, conn, if_exists="replace", index=False)
    conn.close()
    print(f"数据已存储到 {db_name} 的 {table_name} 表中,共 {len(df)} 条记录")
    return df

def clean_house_data(df):
    df = df[(df["total_price"] > 0) & (df["unit_price"] > 0) & (df["area"] > 0)]
    df = df.drop_duplicates(subset=["community", "layout", "area"])
    df["district"] = df["region"].apply(lambda x: x.split(" - ")[0] if " - " in x else x)
    df = df.sort_values(by="total_price", ascending=False).reset_index(drop=True)
    return df

# 数据分析与可视化
def analyze_price_distribution(df):
    plt.figure(figsize=(14, 6))
    plt.subplot(1, 2, 1)
    sns.histplot(df["total_price"], bins=20, kde=True, color="skyblue")
    plt.title("二手房总价分布(万元)", fontsize=14)
    plt.xlabel("总价(万元)", fontsize=12)
    plt.ylabel("房源数量", fontsize=12)
    
    plt.subplot(1, 2, 2)
    sns.histplot(df["unit_price"] / 10000, bins=20, kde=True, color="orange")
    plt.title("二手房单价分布(万元/平米)", fontsize=14)
    plt.xlabel("单价(万元/平米)", fontsize=12)
    plt.ylabel("房源数量", fontsize=12)
    
    plt.tight_layout()
    plt.show()

def analyze_area_vs_price(df):
    plt.figure(figsize=(14, 6))
    plt.subplot(1, 2, 1)
    sns.scatterplot(data=df, x="area", y="total_price", alpha=0.6)
    sns.regplot(data=df, x="area", y="total_price", scatter=False, color="red")
    plt.title("房屋面积与总价的关系", fontsize=14)
    plt.xlabel("面积(平米)", fontsize=12)
    plt.ylabel("总价(万元)", fontsize=12)
    
    plt.subplot(1, 2, 2)
    sns.scatterplot(data=df, x="area", y="unit_price", alpha=0.6)
    sns.regplot(data=df, x="area", y="unit_price", scatter=False, color="red")
    plt.title("房屋面积与单价的关系", fontsize=14)
    plt.xlabel("面积(平米)", fontsize=12)
    plt.ylabel("单价(元/平米)", fontsize=12)
    
    plt.tight_layout()
    plt.show()
    
    area_total_corr = df["area"].corr(df["total_price"])
    area_unit_corr = df["area"].corr(df["unit_price"])
    print(f"面积与总价的相关系数:{area_total_corr:.2f}")
    print(f"面积与单价的相关系数:{area_unit_corr:.2f}")

def analyze_region_layout_impact(df):
    district_price = df.groupby("district")["unit_price"].mean().sort_values(ascending=False) / 10000
    
    plt.figure(figsize=(14, 10))
    plt.subplot(2, 1, 1)
    sns.barplot(x=district_price.index, y=district_price.values, palette="coolwarm")
    plt.title("各区域平均单价(万元/平米)", fontsize=14)
    plt.xlabel("区域", fontsize=12)
    plt.ylabel("平均单价(万元/平米)", fontsize=12)
    plt.xticks(rotation=45)
    
    main_layouts = df["layout"].value_counts()[df["layout"].value_counts() > 5].index
    layout_df = df[df["layout"].isin(main_layouts)]
    layout_price = layout_df.groupby("layout")["unit_price"].mean().sort_values(ascending=False) / 10000
    plt.subplot(2, 1, 2)
    sns.barplot(x=layout_price.index, y=layout_price.values, palette="viridis")
    plt.title("不同户型的平均单价(万元/平米)", fontsize=14)
    plt.xlabel("户型", fontsize=12)
    plt.ylabel("平均单价(万元/平米)", fontsize=12)
    
    plt.tight_layout()
    plt.show()

# 主函数
if __name__ == "__main__":
    # 配置参数(替换为目标城市和区域的链家URL)
    # 例如:上海浦东新区 → https://sh.lianjia.com/ershoufang/pudongxinqu/
    base_url = "https://bj.lianjia.com/ershoufang/haidian/"  # 北京海淀区
    max_page = 5  # 爬取页数
    
    # 爬取数据
    print("===== 开始爬取链家二手房数据 =====")
    houses = crawl_lianjia_houses(base_url, max_page)
    
    # 数据存储与清洗
    print("\n===== 数据存储与清洗 =====")
    df = save_to_sqlite(houses)
    cleaned_df = clean_house_data(df)
    print(f"清洗后的数据量:{len(cleaned_df)} 条")
    
    # 数据分析与可视化
    print("\n===== 房价分布分析 =====")
    analyze_price_distribution(cleaned_df)
    
    print("\n===== 面积与房价关系分析 =====")
    analyze_area_vs_price(cleaned_df)
    
    print("\n===== 区域与户型影响分析 =====")
    analyze_region_layout_impact(cleaned_df)
6.2 使用说明

替换目标 URL:根据需求修改

base_url
为目标城市和区域的链家二手房页面 URL,例如: 上海浦东新区:
https://sh.lianjia.com/ershoufang/pudongxinqu/
广州天河区:
https://gz.lianjia.com/ershoufang/tianhe/

调整爬取参数:修改

max_page
设置爬取的页数(建议单次不超过 10 页,避免触发反爬)。

运行代码:执行脚本后,将自动完成爬取、数据存储、清洗及分析,生成 3 组可视化图表。

反爬注意事项:链家对 IP 访问频率敏感,若出现请求失败,可增加

time.sleep
延迟(如调整为 2-4 秒);若 IP 被限制,可更换网络环境或使用代理 IP。

结果应用:购房者可根据房价分布与区域差异制定预算,投资者可通过面积与单价的关系识别性价比房源,研究者可基于批量数据拓展城市房产市场分析。

七、总结与扩展

7.1 总结

本文以链家二手房数据为研究对象,详细介绍了从数据爬取到分析的完整流程:通过 requests 与 BeautifulSoup 实现房源信息的批量采集,利用 pandas 进行数据清洗与存储,结合 matplotlib 与 seaborn 可视化房价分布、面积与房价的关系及区域 / 户型影响。核心亮点在于通过正则表达式精准提取非结构化文本中的房源特征,并通过多维度分析揭示房价规律,为房产决策提供数据支持。

7.2 扩展方向

多区域对比分析:爬取同一城市不同区域或不同城市的房源数据,对比分析房价差异及影响因素(如经济水平、教育资源)。

时间序列分析:通过定时爬取实现房源数据的时序跟踪,分析房价波动趋势与市场热度变化。

特征工程与预测模型:基于爬取的房源特征(面积、户型、地段等),构建房价预测模型(如线性回归、随机森林)。

学区房溢价分析:结合学区划分数据,量化分析学区资源对房价的溢价影响。

通过本文的学习,读者不仅掌握了房产平台数据的抓取技术,更能学会将数据分析方法应用于房地产市场研究,提高决策的科学性和准确性。

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:python 二手房 distribution orientation scatterplot
相关内容:Python数据分析

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2025-12-9 14:02