首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >数据获取与分析全流程:Python爬取并可视化贝壳成交趋势

数据获取与分析全流程:Python爬取并可视化贝壳成交趋势

原创
作者头像
小白学大数据
发布2025-10-29 16:46:55
发布2025-10-29 16:46:55
1420
举报

一、项目核心思路与技术选型

在动手之前,我们必须明确目标和路径。我们的核心流程是:获取数据 -> 清洗存储 -> 分析可视化

  1. 数据获取:我们将通过Python模拟浏览器请求,直接调用贝壳的隐藏API接口来获取结构化的JSON数据。这种方法比解析整个网页(HTML)更高效、更稳定。核心库是 requests
  2. 数据清洗与存储:API返回的数据并非完美无缺,我们需要进行清洗,处理缺失值、格式化字段(如价格、日期),并将最终结果持久化保存到CSV文件中,便于后续分析。核心库是 pandas
  3. 数据分析与可视化:利用 pandas 进行数据聚合与分组计算,然后使用 pyecharts 库(或 matplotlib)绘制出直观、交互式的图表,揭示趋势和分布。

重要声明(合规性):本文技术内容仅用于学习和研究目的。爬取公开数据时应遵守网站的 robots.txt 协议,控制请求频率,避免对目标网站服务器造成压力。商业使用需获得授权。

二、实战代码:分步解析与实现

让我们开始动手,一步步实现整个流程。

步骤1:环境准备与库安装
步骤2:逆向工程,定位数据API

这是最关键的一步。通过浏览器开发者工具(F12),我们可以在“网络”(Network)选项卡下,筛选XHR/Fetch请求,当浏览贝壳的成交页面时,会发现一个包含“deal”字样的API请求。分析这个请求,我们可以找到其URL、请求头(Headers)和请求参数。

经过分析,我们找到一个模拟的API接口格式。在实际操作中,你需要自行寻找当前有效的接口。

代码语言:javascript
复制
import requests
import pandas as pd
import json
from datetime import datetime
from pyecharts import options as opts
from pyecharts.charts import Bar, Line, Page
import time

# 定义请求头,模拟浏览器行为,降低被反爬的风险
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://bj.ke.com/chengjiao/',  # 根据实际城市修改
    'Accept': 'application/json, text/plain, */*'
}
步骤3:编写爬虫核心函数

我们将编写一个函数,用于抓取特定页码的成交数据。

代码语言:javascript
复制
import requests
import pandas as pd
import json
from datetime import datetime
from pyecharts import options as opts
from pyecharts.charts import Bar, Line, Page
import time

# 代理配置信息
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"

# 代理服务器
proxyMeta = f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"
proxies = {
    "http": proxyMeta,
    "https": proxyMeta,
}

# 定义请求头,模拟浏览器行为,降低被反爬的风险
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://bj.ke.com/chengjiao/',  # 根据实际城市修改
    'Accept': 'application/json, text/plain, */*'
}

def fetch_beike_deal_data(city='bj', page=1):
    """
    爬取贝壳指定城市和页数的成交数据
    :param city: 城市代码,如'bj'代表北京,'sh'代表上海
    :param page: 页码
    :return: 包含成交记录的列表
    """
    # 注意:此URL为示例,实际URL、参数和签名机制需要你通过开发者工具分析获取
    # 贝壳的API通常会有_signature和ts等动态参数,这里简化处理。
    url = f'https://{city}.ke.com/api/listtop'
    
    params = {
        'type': 'deal',
        'page': page,
        # ... 其他必要参数,如区域、排序等
    }
    
    try:
        # 在requests.get中添加proxies参数
        response = requests.get(
            url, 
            headers=headers, 
            params=params, 
            timeout=10,
            proxies=proxies  # 添加代理配置
        )
        response.raise_for_status()  # 如果状态码不是200,抛出异常
        
        data = response.json()
        # 解析返回的JSON数据,提取成交列表
        # 实际结构需要根据API返回结果调整
        deal_list = data.get('data', {}).get('list', [])
        return deal_list
    except requests.exceptions.ProxyError as e:
        print(f"代理连接错误,爬取第{page}页数据时出错: {e}")
        return []
    except requests.exceptions.ConnectTimeout as e:
        print(f"连接超时,爬取第{page}页数据时出错: {e}")
        return []
    except requests.exceptions.RequestException as e:
        print(f"爬取第{page}页数据时出错: {e}")
        return []

# 示例:爬取北京前3页的成交数据
all_deals = []
for page_num in range(1, 4): # 控制爬取页数,避免请求过快
    print(f"正在爬取第{page_num}页...")
    page_data = fetch_beike_deal_data('bj', page_num)
    if page_data:
        all_deals.extend(page_data)
        print(f"第{page_num}页成功爬取到{len(page_data)}条数据")
    else:
        print(f"第{page_num}页爬取失败")
    time.sleep(1)  # 礼貌性延时,非常重要!

print(f"共爬取到{len(all_deals)}条成交记录。")

def clean_and_save_data(deal_list, filename='beike_deals.csv'):
    """
    清洗数据并保存到CSV文件
    """
    if not deal_list:
        print("没有数据需要清洗。")
        return
    
    # 将原始数据列表转换为Pandas DataFrame
    df = pd.DataFrame(deal_list)
    
    # 查看原始列名,根据实际情况选择需要的字段
    print("原始数据列名:", df.columns.tolist())
    
    # 假设我们有的字段如下(请根据实际API响应调整):
    # 'house_code', 'title', 'district', 'street', 'community', 'deal_date', 'total_price', 'unit_price', 'area'
    
    # 1. 选择需要的列
    selected_columns = ['house_code', 'title', 'district', 'street', 'community', 'deal_date', 'total_price', 'unit_price', 'area']
    # 如果某些列不存在,可能需要从其他列中提取
    # 只选择存在的列
    available_columns = [col for col in selected_columns if col in df.columns]
    df = df[available_columns]
    
    # 2. 处理价格和面积:去除单位,转换为数值类型
    if 'total_price' in df.columns:
        df['total_price'] = df['total_price'].astype(str).str.replace('万', '').astype(float)
    if 'unit_price' in df.columns:
        df['unit_price'] = df['unit_price'].astype(str).str.replace('元/平', '').str.replace(',', '').astype(float)
    if 'area' in df.columns:
        df['area'] = df['area'].astype(str).str.replace('平米', '').astype(float)
    
    # 3. 处理日期:转换为datetime格式
    if 'deal_date' in df.columns:
        df['deal_date'] = pd.to_datetime(df['deal_date'])
    
    # 4. 去重
    if 'house_code' in df.columns:
        df.drop_duplicates(subset=['house_code'], inplace=True)
    
    # 5. 处理缺失值
    df.dropna(inplace=True)
    
    # 保存清洗后的数据
    df.to_csv(filename, index=False, encoding='utf-8-sig')
    print(f"数据已清洗并保存到 {filename}")
    return df

# 执行清洗和保存
if all_deals:
    cleaned_df = clean_and_save_data(all_deals)
else:
    print("没有成功爬取到数据,请检查代理配置或网络连接。")
步骤4:数据清洗与存储

爬取到的原始数据往往是杂乱无章的,我们需要将其“驯服”。

代码语言:javascript
复制
def clean_and_save_data(deal_list, filename='beike_deals.csv'):
    """
    清洗数据并保存到CSV文件
    """
    if not deal_list:
        print("没有数据需要清洗。")
        return
    
    # 将原始数据列表转换为Pandas DataFrame
    df = pd.DataFrame(deal_list)
    
    # 查看原始列名,根据实际情况选择需要的字段
    print("原始数据列名:", df.columns.tolist())
    
    # 假设我们有的字段如下(请根据实际API响应调整):
    # 'house_code', 'title', 'district', 'street', 'community', 'deal_date', 'total_price', 'unit_price', 'area'
    
    # 1. 选择需要的列
    selected_columns = ['house_code', 'title', 'district', 'street', 'community', 'deal_date', 'total_price', 'unit_price', 'area']
    # 如果某些列不存在,可能需要从其他列中提取
    df = df[selected_columns]
    
    # 2. 处理价格和面积:去除单位,转换为数值类型
    df['total_price'] = df['total_price'].str.replace('万', '').astype(float)
    df['unit_price'] = df['unit_price'].str.replace('元/平', '').str.replace(',', '').astype(float)
    df['area'] = df['area'].str.replace('平米', '').astype(float)
    
    # 3. 处理日期:转换为datetime格式
    df['deal_date'] = pd.to_datetime(df['deal_date'])
    
    # 4. 去重
    df.drop_duplicates(subset=['house_code'], inplace=True)
    
    # 5. 处理缺失值
    df.dropna(inplace=True)
    
    # 保存清洗后的数据
    df.to_csv(filename, index=False, encoding='utf-8-sig')
    print(f"数据已清洗并保存到 {filename}")
    return df

# 执行清洗和保存
if all_deals:
    cleaned_df = clean_and_save_data(all_deals)
步骤5:数据分析与可视化

现在,是时候让数据“开口说话”了。我们使用 pyecharts 来创建交互式图表。

代码语言:javascript
复制
def create_visualizations(df):
    """
    创建可视化图表
    """
    if df is None or df.empty:
        print("没有有效数据进行可视化。")
        return
    
    page = Page(layout=Page.SimplePageLayout) # 创建一个页面,用于组合所有图表

    # 1. 月度成交趋势折线图
    # 按月份聚合数据
    df['deal_month'] = df['deal_date'].dt.to_period('M').astype(str)
    monthly_trend = df.groupby('deal_month').agg({'house_code': 'count', 'unit_price': 'mean'}).reset_index()
    monthly_trend.columns = ['month', 'deal_count', 'avg_unit_price']
    
    line = (
        Line()
        .add_xaxis(monthly_trend['month'].tolist())
        .add_yaxis("成交套数",
                   monthly_trend['deal_count'].tolist(),
                   yaxis_index=0,
                   color="#d14a61",
                   label_opts=opts.LabelOpts(is_show=False))
        .extend_axis( # 添加第二个Y轴用于单价
            yaxis=opts.AxisOpts(
                name="均价(元/平)",
                type_="value",
                min_=max(0, monthly_trend['avg_unit_price'].min() * 0.9),
                axislabel_opts=opts.LabelOpts(formatter="{value}"),
            )
        )
        .add_yaxis("成交均价",
                   monthly_trend['avg_unit_price'].round(2).tolist(),
                   yaxis_index=1,
                   color="#5793f3",
                   label_opts=opts.LabelOpts(is_show=False))
        .set_global_opts(
            title_opts=opts.TitleOpts(title="贝壳成交数据月度趋势"),
            tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"),
            xaxis_opts=opts.AxisOpts(type_="category", axislabel_opts=opts.LabelOpts(rotate=45)),
            yaxis_opts=opts.AxisOpts(name="成交套数", axislabel_opts=opts.LabelOpts(formatter="{value} 套")),
        )
    )
    page.add(line)

    # 2. 各区成交数量柱状图
    district_count = df['district'].value_counts()
    bar = (
        Bar()
        .add_xaxis(district_count.index.tolist())
        .add_yaxis("成交数量", district_count.values.tolist())
        .set_global_opts(
            title_opts=opts.TitleOpts(title="各区域成交数量分布"),
            xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15)),
            visualmap_opts=opts.VisualMapOpts(max_=district_count.max())
        )
    )
    page.add(bar)

    # 3. 价格分布直方图 (使用Bar图模拟)
    price_bins = pd.cut(df['unit_price'], bins=10).value_counts().sort_index()
    bar_price = (
        Bar()
        .add_xaxis([str(x) for x in price_bins.index])
        .add_yaxis("房源数量", price_bins.values.tolist())
        .set_global_opts(
            title_opts=opts.TitleOpts(title="单位价格分布"),
            xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45)),
        )
    )
    page.add(bar_price)

    # 渲染所有图表到一个HTML文件
    page.render("beike_deal_analysis.html")
    print("可视化图表已生成到 beike_deal_analysis.html")

# 执行可视化
if 'cleaned_df' in locals():
    create_visualizations(cleaned_df)

三、成果解读与商业价值

运行完上述代码后,你将得到一个名为 beike_deal_analysis.html 的交互式网页。打开它,你可以:

  • 洞察市场周期:通过“月度成交趋势”图,清晰地看到成交量与价格随时间的波动。是持续上涨,还是进入平台期?成交量与价格是同步变化还是出现背离?(例如,价升量跌可能预示市场见顶)。
  • 识别热点区域:“各区域成交数量分布”图直接告诉你哪个区的交易最活跃,这对于投资选址和判断区域流动性至关重要。
  • 把握价格区间:“单位价格分布”图展示了主流成交价集中在哪个区间。购房者可以据此判断自己的预算是否合理,开发商可以了解市场的接受度。

从商业角度看,这套自动化流程可以:

  • 为个人购房者提供客观的决策依据,避免被市场情绪和片面信息误导。
  • 为房产投资者发现被低估的板块和物业类型。
  • 为市场研究人员提供长期、连续的数据源,用于构建更复杂的预测模型。
  • 为中介机构分析自身在市场中的份额和竞争态势。

四、总结与展望

本文详细演示了一个端到端的数据项目:使用Python爬取贝壳成交数据,并进行可视化分析。我们不仅完成了技术实现,更强调了从数据中提炼商业洞察的思路。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、项目核心思路与技术选型
  • 二、实战代码:分步解析与实现
    • 步骤1:环境准备与库安装
    • 步骤2:逆向工程,定位数据API
    • 步骤3:编写爬虫核心函数
    • 步骤4:数据清洗与存储
    • 步骤5:数据分析与可视化
  • 三、成果解读与商业价值
  • 四、总结与展望
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档