Back to skills
extension
Category: OtherNo API key required

家福找房

家福找房提供优质房源信息

personAuthor: user_ef8fcd00hubcommunity

Python 接口请求技能文档

1. 功能说明

技能用途

本技能用于通过 Python 的 requests 库发送 HTTP POST 请求,获取二手房房源数据接口的数据,并重点解决接口返回的中文乱码问题。

适用场景

  • 需要从 RESTful API 接口获取结构化数据

  • 接口返回 JSON 格式数据但存在中文乱码问题

  • 需要批量获取和处理房产类数据

  • 适用于数据分析、爬虫、数据同步等场景

依赖环境

  • Python 3.6+

  • requests 库 (pip install requests)


2. Python 实现代码

import requests
import json

def get_second_hand_house_data(page=1, limit=10, city_code="431300"):
    """
    获取二手房房源列表数据
    
    Args:
        page (int): 页码,默认第1页
        limit (int): 每页数据条数,默认10条
        city_code (str): 城市编码,默认娄底市 431300
        
    Returns:
        dict: 解析后的JSON数据,包含房源列表信息
    """
    url = "https://zfapi.jiafudc.com/api/SecondHandSellHouse/GetSecondHandSellHouseList"
    
    # 请求头设置
    headers = {
        'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
        'Accept': '*/*',
        'Host': 'zfapi.jiafudc.com',
        'Connection': 'keep-alive',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    # 请求参数
    payload = {
        'page': page,
        'limit': limit,
        'cityCode': city_code,
        'sortField': 'm_ALLFloor',
        'unknownUserId': '20fcfd01-3b88-bcaa-305b-a590780d30da'
    }
    
    try:
        # 发送POST请求
        response = requests.post(url, headers=headers, data=payload, timeout=10)
        
        # ==================== 中文乱码核心处理 ====================
        # 方案1:手动指定编码(推荐,最可靠)
        response.encoding = 'utf-8'
        
        # 方案2:自动检测编码(备选,适用于不确定编码的情况)
        # response.encoding = response.apparent_encoding
        
        # 方案3:手动解码(终极方案,处理特殊编码)
        # content = response.content.decode('utf-8', errors='ignore')
        # =========================================================
        
        # 检查请求是否成功
        response.raise_for_status()
        
        # 解析JSON数据
        result = response.json()
        
        print(f"请求成功,获取到 {len(result.get('data', {}).get('list', []))} 条数据")
        return result
        
    except requests.exceptions.RequestException as e:
        print(f"请求失败: {str(e)}")
        return None
    except json.JSONDecodeError as e:
        print(f"JSON解析失败: {str(e)}")
        print(f"响应内容预览: {response.text[:200]}")
        return None


# 示例调用
if __name__ == "__main__":
    # 获取第1页,每页5条数据
    data = get_second_hand_house_data(page=1, limit=5)
    
    if data:
        # 打印返回结果的结构
        print("\n=== 返回数据结构 ===")
        print(f"状态码: {data.get('code')}")
        print(f"消息: {data.get('msg')}")
        
        # 打印房源数据
        house_list = data.get('data', {}).get('list', [])
        print(f"\n=== 房源列表示例(前2条)===")
        for i, house in enumerate(house_list[:2], 1):
            print(f"\n--- 房源 {i} ---")
            print(f"标题: {house.get('m_Title', 'N/A')}")
            print(f"价格: {house.get('m_Price', 'N/A')} 万元")
            print(f"面积: {house.get('m_Square', 'N/A')} ㎡")
            print(f"户型: {house.get('m_Room', 'N/A')}{house.get('m_Hall', 'N/A')}厅")
            print(f"楼层: {house.get('m_Floor', 'N/A')}/{house.get('m_ALLFloor', 'N/A')}层")
            print(f"小区: {house.get('m_Address', 'N/A')}")

3. 中文乱码处理的详细说明和解决方案

3.1 为什么会出现中文乱码?

中文乱码的根本原因是编码和解码使用的字符集不一致。在 HTTP 请求场景中,具体表现为:

|乱码原因|说明| |---|---| |编码不匹配|服务器实际使用 UTF-8 编码返回数据,但 requests 库误认为是 ISO-8859-1(Latin-1)编码| |响应头缺失|服务器返回的 Content\-Type 响应头中没有指定 charset 信息| |自动检测失败|requests 库的编码自动检测算法(chardet)判断错误| |特殊编码|部分国内网站使用 GBK/GB2312/GB18030 编码而非 UTF-8| |BOM 头问题|UTF-8 BOM 头导致解析异常|

典型乱码表现:

  • 中文 这样的乱码字符(UTF-8 被当作 Latin-1 解析)

  • ?????? 问号(编码不支持中文字符)

  • 锟斤拷 经典乱码(GBK 和 UTF-8 转换错误)


3.2 多种解决方案对比

方案 1:手动指定编码(推荐,最可靠)

response.encoding = 'utf-8'
  • 优点:性能最好,直接指定正确编码,无需检测

  • 适用场景:明确知道接口使用 UTF-8 编码(绝大多数现代接口)

  • 成功率:95% 以上的中文乱码可以通过此方法解决

方案 2:使用 apparent_encoding 自动检测

response.encoding = response.apparent_encoding
  • 优点:自动检测,无需人工判断

  • 原理:使用 chardet 库分析响应内容的字节特征来判断编码

  • 适用场景:不确定接口编码时的兜底方案

  • 注意:会增加少量性能开销,大响应体时明显

方案 3:手动解码字节内容(终极方案)

# 方式A:直接解码
content = response.content.decode('utf-8', errors='ignore')

# 方式B:尝试多种编码
encodings = ['utf-8', 'gbk', 'gb2312', 'gb18030']
for enc in encodings:
    try:
        content = response.content.decode(enc)
        break
    except UnicodeDecodeError:
        continue
  • 优点:最灵活,可以处理各种极端情况

  • 适用场景:前两种方案都失败时

  • errors='ignore':忽略无法解码的字符,避免程序崩溃

方案 4:针对 GBK 编码的特殊处理

# 部分老旧系统使用 GBK 编码
response.encoding = 'gbk'
# 或者
response.encoding = 'gb18030'  # GBK 的超集,支持更多字符

3.3 调试乱码问题的技巧

def debug_encoding(response):
    """调试编码问题的辅助函数"""
    print("=== 编码调试信息 ===")
    print(f"requests 猜测的编码: {response.encoding}")
    print(f"自动检测的编码: {response.apparent_encoding}")
    print(f"Content-Type 头: {response.headers.get('Content-Type')}")
    print(f"响应字节前50个: {response.content[:50]}")
    print(f"当前文本前50字符: {response.text[:50]}")
    
    # 尝试不同编码
    for enc in ['utf-8', 'gbk', 'gb2312', 'latin-1']:
        try:
            decoded = response.content.decode(enc)[:30]
            print(f"{enc} 解码结果: {decoded}")
        except:
            print(f"{enc} 解码失败")

# 使用方法:在请求后调用
debug_encoding(response)

4. 请求参数说明

|参数名|类型|必填|默认值|说明| |---|---|---|---|---| |page|int|是|1|页码,从 1 开始计数。用于分页获取数据| |limit|int|是|10|每页返回的数据条数。建议设置为 10-50 之间,过大可能导致请求超时| |cityCode|string|是|431300|城市行政区划代码:<br>- 431300 = 娄底市<br>- 可根据需要更换为其他城市代码| |sortField|string|是|m_ALLFloor|排序字段:<br>- m\_ALLFloor: 按总楼层排序<br>- 其他可选值需参考接口文档| |unknownUserId|string|是|-|用户标识 ID,用于接口访问鉴权和请求追踪。此为固定值,请勿随意修改|

参数使用建议

  1. 分页策略:建议 limit 设置为 20-30,循环请求多页数据,避免单次请求过大

  2. 城市代码:可通过国家统计局官网查询各城市的行政区划代码

  3. 频率控制:请求频率建议控制在 1 秒 / 次以内,避免触发接口限流


5. 返回结果处理示例

5.1 返回数据结构说明

接口返回标准 JSON 格式,结构如下:

{
  "code": 0,              // 状态码,0 表示成功
  "msg": "success",       // 状态消息
  "data": {
    "total": 1234,        // 数据总条数
    "page": 1,            // 当前页码
    "limit": 10,          // 每页条数
    "list": [             // 房源列表数组
      {
        "m_ID": "xxx",    // 房源ID
        "m_Title": "xxx", // 房源标题
        "m_Price": 100,   // 价格(万元)
        "m_Square": 120,  // 面积(㎡)
        "m_Room": 3,      // 室数
        "m_Hall": 2,      // 厅数
        "m_Floor": 15,    // 当前楼层
        "m_ALLFloor": 32, // 总楼层
        "m_Address": "xxx",// 小区地址
        "m_Phone": "xxx"  // 联系电话
        // ... 其他字段
      }
    ]
  }
}

5.2 数据处理示例代码

def process_house_data(result):
    """
    处理返回的房源数据,提取关键信息并格式化输出
    """
    if not result or result.get('code') != 0:
        print("数据获取失败或返回错误")
        return []
    
    data = result.get('data', {})
    house_list = data.get('list', [])
    
    processed_data = []
    
    for house in house_list:
        # 提取关键字段,设置默认值避免 KeyError
        processed_house = {
            '房源ID': house.get('m_ID', ''),
            '房源标题': house.get('m_Title', '').strip(),
            '总价(万元)': house.get('m_Price', 0),
            '面积(㎡)': house.get('m_Square', 0),
            '户型': f"{house.get('m_Room', 0)}{house.get('m_Hall', 0)}厅",
            '楼层': f"{house.get('m_Floor', 0)}/{house.get('m_ALLFloor', 0)}层",
            '小区地址': house.get('m_Address', ''),
            '单价(元/㎡)': round(house.get('m_Price', 0) * 10000 / house.get('m_Square', 1), 2) 
                          if house.get('m_Square', 0) > 0 else 0
        }
        processed_data.append(processed_house)
    
    return processed_data


def export_to_csv(data, filename='house_data.csv'):
    """
    将处理后的数据导出为CSV文件
    """
    import csv
    
    if not data:
        print("没有数据可导出")
        return
    
    with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
        writer = csv.DictWriter(f, fieldnames=data[0].keys())
        writer.writeheader()
        writer.writerows(data)
    
    print(f"数据已导出到 {filename}")


# 综合使用示例
if __name__ == "__main__":
    # 获取数据
    raw_data = get_second_hand_house_data(page=1, limit=20)
    
    if raw_data:
        # 处理数据
        processed = process_house_data(raw_data)
        
        # 打印统计信息
        print(f"\n=== 数据统计 ===")
        total = raw_data.get('data', {}).get('total', 0)
        print(f"总房源数: {total} 套")
        print(f"本次获取: {len(processed)} 套")
        
        if processed:
            avg_price = sum(h['总价(万元)'] for h in processed) / len(processed)
            avg_unit_price = sum(h['单价(元/㎡)'] for h in processed) / len(processed)
            print(f"平均总价: {avg_price:.1f} 万元")
            print(f"平均单价: {avg_unit_price:.0f} 元/㎡")
        
        # 导出CSV
        export_to_csv(processed)

5.3 批量获取所有数据

def get_all_house_data(max_pages=10):
    """
    循环获取所有页面的数据
    """
    all_data = []
    
    for page in range(1, max_pages + 1):
        print(f"正在获取第 {page} 页...")
        result = get_second_hand_house_data(page=page, limit=20)
        
        if not result or result.get('code') != 0:
            print(f"第 {page} 页获取失败,停止获取")
            break
        
        house_list = result.get('data', {}).get('list', [])
        if not house_list:
            print("已获取完所有数据")
            break
        
        all_data.extend(house_list)
        
        # 检查是否还有下一页
        total = result.get('data', {}).get('total', 0)
        if len(all_data) >= total:
            break
        
        # 避免请求过快
        import time
        time.sleep(0.5)
    
    print(f"共获取 {len(all_data)} 条房源数据")
    return all_data

6. 常见问题与注意事项

  1. 请求频率限制:建议添加适当的延时(0.5-1 秒),避免触发服务器反爬机制

  2. 异常处理:网络请求可能失败,务必添加 try-except 异常捕获

  3. 参数验证:cityCode 等参数需要验证有效性,避免传入错误值

  4. 数据清洗:返回数据可能存在空值或异常值,处理时需设置默认值

  5. 编码问题:如果 UTF-8 不行,尝试 GBK 编码,部分老旧系统可能使用 GBK

(注:文档部分内容可能由 AI 生成)