汽车之家item_search - 根据地区获取二手车列表接口对接全攻略:从入门到精通

一、接口概览

1.1 接口简介

item_search接口是汽车之家开放平台的核心接口之一,专门用于二手车信息检索。支持按地区、品牌、价格、车龄等多维度筛选二手车,返回结构化的车辆列表信息。

1.2 核心功能

  • 地区筛选:按省市区精准定位二手车源

  • 多条件搜索:品牌、车系、价格、里程、车龄等

  • 分页查询:支持大数据量的分页加载

  • 排序功能:按价格、里程、发布时间等排序

  • 字段选择:可指定返回字段,优化网络传输

二、准备工作

2.1 环境配置

复制代码
# requirements.txt
requests>=2.28.0
python-dotenv>=1.0.0
pydantic>=2.0.0
aiohttp>=3.8.0
redis>=4.5.0

2.2 认证配置

复制代码
# config.py
import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    # 汽车之家API配置
    AUTOHOME_APP_KEY = os.getenv('AUTOHOME_APP_KEY')
    AUTOHOME_APP_SECRET = os.getenv('AUTOHOME_APP_SECRET')
    AUTOHOME_API_BASE = os.getenv('AUTOHOME_API_BASE', 
        'https://openapi.autohome.com.cn/api/v1'
    )
    
    # 请求配置
    REQUEST_TIMEOUT = 30
    MAX_RETRIES = 3
    DEFAULT_PAGE_SIZE = 20
    MAX_PAGE_SIZE = 100
    
    # 缓存配置
    CACHE_TTL = 3600  # 1小时

三、接口详解

3.1 接口地址

复制代码
GET /usedcar/search

3.2 请求参数详解

基础参数
参数名 类型 必填 说明 示例
app_key string 应用标识 autohome_app_2024
timestamp int 时间戳 1706774400
sign string 请求签名 详见签名算法
format string 返回格式 json(默认)
version string API版本 1.0
搜索参数
参数名 类型 必填 说明 示例
province_id int 省份ID 11(北京)
city_id int 城市ID 1101(北京市)
district_id int 区县ID 110101(东城区)
brand_id int 品牌ID 1(奥迪)
series_id int 车系ID 10(A6L)
min_price float 最低价格(万元) 10.0
max_price float 最高价格(万元) 50.0
min_mileage int 最低里程(万公里) 1
max_mileage int 最高里程(万公里) 20
min_year int 最低年份 2018
max_year int 最高年份 2023
fuel_type string 燃油类型 汽油/柴油/新能源
transmission string 变速箱类型 自动/手动
body_type string 车身类型 轿车/SUV/MPV
keyword string 搜索关键词 "奥迪A6L 2020款"
sort_field string 排序字段 price/mileage/year/publish_time
sort_order string 排序方向 asc/desc(默认desc)
page_no int 页码 1(默认)
page_size int 每页条数 20(默认)
fields string 返回字段 id,title,price,mileage,year

四、完整代码实现

4.1 Python完整实现

python 复制代码
import requests
import time
import hashlib
import hmac
import json
from typing import Dict, Any, List, Optional
from datetime import datetime, timedelta
from dataclasses import dataclass
from urllib.parse import urlencode
import redis

@dataclass
class UsedCarBasicInfo:
    """二手车基本信息"""
    car_id: int
    title: str
    price: float
    original_price: float
    mileage: float
    year: int
    month: int
    city: str
    district: str
    brand: str
    series: str
    model: str
    fuel_type: str
    transmission: str
    body_type: str
    publish_time: str
    thumbnail_url: str
    source_type: str  # 个人/商家

@dataclass
class UsedCarDetail:
    """二手车详细信息"""
    basic_info: UsedCarBasicInfo
    images: List[str]
    specs: Dict[str, str]
    seller_info: Dict[str, Any]
    inspection_report: Dict[str, Any]

@dataclass
class SearchResult:
    """搜索结果"""
    success: bool
    code: int
    message: str
    data: Dict[str, Any]
    used_cars: List[UsedCarBasicInfo]
    pagination: Dict[str, Any]

class AutoHomeUsedCarAPI:
    """汽车之家二手车API客户端"""
    
    def __init__(self, app_key: str, app_secret: str, sandbox: bool = True, redis_client=None):
        self.app_key = app_key
        self.app_secret = app_secret
        self.base_url = "https://sandbox-openapi.autohome.com.cn" if sandbox else "https://openapi.autohome.com.cn"
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'AutoHome-UsedCar-API/1.0',
            'Accept': 'application/json'
        })
        self.redis = redis_client
        self._access_token = None
        self._token_expires = None
    
    def _generate_signature(self, params: Dict[str, Any], timestamp: int) -> str:
        """生成请求签名"""
        # 排序参数
        sorted_params = sorted(params.items())
        param_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
        
        # 构建签名字符串
        sign_str = f"{self.app_key}{param_str}{timestamp}{self.app_secret}"
        
        # 计算HMAC-SHA256签名
        signature = hmac.new(
            self.app_secret.encode('utf-8'),
            sign_str.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
        
        return signature
    
    def _get_access_token(self) -> str:
        """获取访问令牌"""
        # 检查token是否有效
        if self._access_token and self._token_expires and self._token_expires > datetime.now():
            return self._access_token
        
        # 获取新token
        timestamp = int(time.time())
        params = {
            'app_key': self.app_key,
            'timestamp': timestamp,
            'grant_type': 'client_credentials'
        }
        
        # 生成签名
        signature = self._generate_signature(params, timestamp)
        params['sign'] = signature
        
        # 请求token
        url = f"{self.base_url}/oauth/token"
        response = self.session.post(url, data=params)
        
        if response.status_code == 200:
            data = response.json()
            self._access_token = data['access_token']
            self._token_expires = datetime.now() + timedelta(seconds=data['expires_in'] - 300)  # 提前5分钟过期
            return self._access_token
        else:
            raise Exception(f"获取token失败: {response.status_code} - {response.text}")
    
    def search_used_cars(
        self,
        province_id: Optional[int] = None,
        city_id: Optional[int] = None,
        district_id: Optional[int] = None,
        brand_id: Optional[int] = None,
        series_id: Optional[int] = None,
        min_price: Optional[float] = None,
        max_price: Optional[float] = None,
        min_mileage: Optional[float] = None,
        max_mileage: Optional[float] = None,
        min_year: Optional[int] = None,
        max_year: Optional[int] = None,
        fuel_type: Optional[str] = None,
        transmission: Optional[str] = None,
        body_type: Optional[str] = None,
        keyword: Optional[str] = None,
        sort_field: str = "publish_time",
        sort_order: str = "desc",
        page_no: int = 1,
        page_size: int = 20,
        fields: Optional[List[str]] = None
    ) -> SearchResult:
        """
        搜索二手车
        
        Args:
            province_id: 省份ID
            city_id: 城市ID
            district_id: 区县ID
            brand_id: 品牌ID
            series_id: 车系ID
            min_price: 最低价格(万元)
            max_price: 最高价格(万元)
            min_mileage: 最低里程(万公里)
            max_mileage: 最高里程(万公里)
            min_year: 最低年份
            max_year: 最高年份
            fuel_type: 燃油类型
            transmission: 变速箱类型
            body_type: 车身类型
            keyword: 搜索关键词
            sort_field: 排序字段
            sort_order: 排序方向
            page_no: 页码
            page_size: 每页条数
            fields: 返回字段列表
        
        Returns:
            搜索结果
        """
        # 获取访问令牌
        access_token = self._get_access_token()
        
        # 构建请求参数
        params = {
            'app_key': self.app_key,
            'timestamp': int(time.time()),
            'format': 'json',
            'version': '1.0',
            'sort_field': sort_field,
            'sort_order': sort_order,
            'page_no': page_no,
            'page_size': min(page_size, Config.MAX_PAGE_SIZE)
        }
        
        # 添加可选参数
        if province_id:
            params['province_id'] = province_id
        if city_id:
            params['city_id'] = city_id
        if district_id:
            params['district_id'] = district_id
        if brand_id:
            params['brand_id'] = brand_id
        if series_id:
            params['series_id'] = series_id
        if min_price:
            params['min_price'] = min_price
        if max_price:
            params['max_price'] = max_price
        if min_mileage:
            params['min_mileage'] = min_mileage
        if max_mileage:
            params['max_mileage'] = max_mileage
        if min_year:
            params['min_year'] = min_year
        if max_year:
            params['max_year'] = max_year
        if fuel_type:
            params['fuel_type'] = fuel_type
        if transmission:
            params['transmission'] = transmission
        if body_type:
            params['body_type'] = body_type
        if keyword:
            params['keyword'] = keyword
        if fields:
            params['fields'] = ','.join(fields)
        
        # 生成签名
        signature = self._generate_signature(params, params['timestamp'])
        params['sign'] = signature
        
        # 添加认证头
        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json'
        }
        
        # 发送请求
        url = f"{self.base_url}/api/v1/usedcar/search"
        
        try:
            response = self.session.get(
                url,
                params=params,
                headers=headers,
                timeout=Config.REQUEST_TIMEOUT
            )
            
            if response.status_code == 200:
                result = response.json()
                
                # 解析结果
                used_cars = self._parse_used_cars(result.get('data', {}).get('list', []))
                pagination = result.get('data', {}).get('pagination', {})
                
                return SearchResult(
                    success=result.get('success', False),
                    code=result.get('code', 0),
                    message=result.get('message', ''),
                    data=result.get('data', {}),
                    used_cars=used_cars,
                    pagination=pagination
                )
            elif response.status_code == 401:
                # Token过期,重新获取
                self._access_token = None
                return self.search_used_cars(
                    province_id=province_id, city_id=city_id, district_id=district_id,
                    brand_id=brand_id, series_id=series_id, min_price=min_price, max_price=max_price,
                    min_mileage=min_mileage, max_mileage=max_mileage, min_year=min_year, max_year=max_year,
                    fuel_type=fuel_type, transmission=transmission, body_type=body_type, keyword=keyword,
                    sort_field=sort_field, sort_order=sort_order, page_no=page_no, page_size=page_size,
                    fields=fields
                )
            else:
                return SearchResult(
                    success=False,
                    code=response.status_code,
                    message=f"HTTP {response.status_code}",
                    data={},
                    used_cars=[],
                    pagination={}
                )
                
        except requests.exceptions.Timeout:
            return SearchResult(
                success=False,
                code=408,
                message="请求超时",
                data={},
                used_cars=[],
                pagination={}
            )
        except requests.exceptions.RequestException as e:
            return SearchResult(
                success=False,
                code=500,
                message=f"网络请求异常: {str(e)}",
                data={},
                used_cars=[],
                pagination={}
            )
    
    def _parse_used_cars(self, car_list: List[Dict[str, Any]]) -> List[UsedCarBasicInfo]:
        """解析二手车列表数据"""
        used_cars = []
        
        for car_data in car_list:
            try:
                basic_info = UsedCarBasicInfo(
                    car_id=car_data.get('id'),
                    title=car_data.get('title', ''),
                    price=car_data.get('price', 0),
                    original_price=car_data.get('original_price', 0),
                    mileage=car_data.get('mileage', 0),
                    year=car_data.get('year', 0),
                    month=car_data.get('month', 0),
                    city=car_data.get('city', ''),
                    district=car_data.get('district', ''),
                    brand=car_data.get('brand', {}).get('name', ''),
                    series=car_data.get('series', {}).get('name', ''),
                    model=car_data.get('model', ''),
                    fuel_type=car_data.get('fuel_type', ''),
                    transmission=car_data.get('transmission', ''),
                    body_type=car_data.get('body_type', ''),
                    publish_time=car_data.get('publish_time', ''),
                    thumbnail_url=car_data.get('thumbnail_url', ''),
                    source_type=car_data.get('source_type', '个人')
                )
                used_cars.append(basic_info)
            except Exception as e:
                print(f"解析车辆数据失败: {e}, 数据: {car_data}")
                continue
        
        return used_cars
    
    def search_all_used_cars(
        self,
        max_pages: int = 10,
        **search_params
    ) -> List[UsedCarBasicInfo]:
        """
        获取所有符合条件的二手车(自动处理分页)
        
        Args:
            max_pages: 最大页数限制
            **search_params: 搜索参数
        
        Returns:
            二手车列表
        """
        all_cars = []
        page_no = 1
        
        while page_no <= max_pages:
            result = self.search_used_cars(page_no=page_no, **search_params)
            
            if not result.success:
                print(f"第{page_no}页查询失败: {result.message}")
                break
            
            # 添加当前页数据
            all_cars.extend(result.used_cars)
            pagination = result.pagination
            
            print(f"已获取第{page_no}页,共{len(result.used_cars)}条,总计{len(all_cars)}条")
            
            # 检查是否还有下一页
            has_next = pagination.get('has_next', False)
            total_pages = pagination.get('total_pages', 0)
            
            if not has_next or page_no >= total_pages:
                break
            
            page_no += 1
            
            # 避免请求过于频繁
            time.sleep(0.5)
        
        return all_cars
    
    def get_used_car_detail(self, car_id: int) -> Optional[UsedCarDetail]:
        """
        获取二手车详细信息
        """
        # 获取访问令牌
        access_token = self._get_access_token()
        
        # 构建请求
        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json'
        }
        
        url = f"{self.base_url}/api/v1/usedcar/detail/{car_id}"
        
        try:
            response = self.session.get(url, headers=headers, timeout=30)
            
            if response.status_code == 200:
                data = response.json().get('data', {})
                
                # 解析基础信息
                basic_info = UsedCarBasicInfo(
                    car_id=data.get('id'),
                    title=data.get('title', ''),
                    price=data.get('price', 0),
                    original_price=data.get('original_price', 0),
                    mileage=data.get('mileage', 0),
                    year=data.get('year', 0),
                    month=data.get('month', 0),
                    city=data.get('city', ''),
                    district=data.get('district', ''),
                    brand=data.get('brand', {}).get('name', ''),
                    series=data.get('series', {}).get('name', ''),
                    model=data.get('model', ''),
                    fuel_type=data.get('fuel_type', ''),
                    transmission=data.get('transmission', ''),
                    body_type=data.get('body_type', ''),
                    publish_time=data.get('publish_time', ''),
                    thumbnail_url=data.get('thumbnail_url', ''),
                    source_type=data.get('source_type', '个人')
                )
                
                # 解析详细信息
                images = data.get('images', [])
                specs = data.get('specs', {})
                seller_info = data.get('seller_info', {})
                inspection_report = data.get('inspection_report', {})
                
                return UsedCarDetail(
                    basic_info=basic_info,
                    images=images,
                    specs=specs,
                    seller_info=seller_info,
                    inspection_report=inspection_report
                )
            else:
                return None
                
        except Exception as e:
            print(f"获取车辆详情失败: {e}")
            return None

# 使用示例
def demo_used_car_api():
    """二手车API使用演示"""
    
    # 初始化客户端
    client = AutoHomeUsedCarAPI(
        app_key=Config.AUTOHOME_APP_KEY,
        app_secret=Config.AUTOHOME_APP_SECRET,
        sandbox=True
    )
    
    print("=== 示例1:按地区搜索二手车 ===")
    result = client.search_used_cars(
        province_id=11,  # 北京
        city_id=1101,    # 北京市
        min_price=10,
        max_price=30,
        min_year=2018,
        max_year=2022,
        page_size=5
    )
    
    if result.success:
        for car in result.used_cars:
            print(f"{car.brand} {car.series} - {car.price}万 - {car.mileage}万公里 - {car.year}年")
    
    print("\n=== 示例2:按品牌搜索 ===")
    result = client.search_used_cars(
        brand_id=1,  # 奥迪
        min_price=20,
        max_price=50,
        sort_field="price",
        sort_order="asc"
    )
    
    print("\n=== 示例3:获取所有符合条件的二手车 ===")
    all_cars = client.search_all_used_cars(
        province_id=11,
        min_price=15,
        max_price=25,
        min_year=2019,
        max_mileage=15
    )
    print(f"共找到 {len(all_cars)} 辆符合条件的二手车")

if __name__ == "__main__":
    demo_used_car_api()

4.2 Java实现

java 复制代码
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex 
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class AutoHomeUsedCarClient {
    private static final Logger logger = LoggerFactory.getLogger(AutoHomeUsedCarClient.class);
    
    private final String appKey;
    private final String appSecret;
    private final String baseUrl;
    private final OkHttpClient httpClient;
    private final ObjectMapper objectMapper;
    
    private String accessToken;
    private LocalDateTime tokenExpires;
    
    public AutoHomeUsedCarClient(String appKey, String appSecret, boolean sandbox) {
        this.appKey = appKey;
        this.appSecret = appSecret;
        this.baseUrl = sandbox ? 
            "https://sandbox-openapi.autohome.com.cn" : 
            "https://openapi.autohome.com.cn";
        
        this.httpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
        
        this.objectMapper = new ObjectMapper();
        this.objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }
    
    public UsedCarSearchResult searchUsedCars(UsedCarSearchParams params) throws IOException {
        // 获取访问令牌
        String token = getAccessToken();
        # 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex 
        // 构建请求参数
        Map<String, Object> requestParams = new HashMap<>();
        requestParams.put("app_key", appKey);
        requestParams.put("timestamp", System.currentTimeMillis() / 1000);
        requestParams.put("format", "json");
        requestParams.put("version", "1.0");
        
        // 添加搜索参数
        if (params.getProvinceId() != null) {
            requestParams.put("province_id", params.getProvinceId());
        }
        if (params.getCityId() != null) {
            requestParams.put("city_id", params.getCityId());
        }
        // ... 其他参数
        
        // 生成签名
        String signature = generateSignature(requestParams, (Long) requestParams.get("timestamp"));
        requestParams.put("sign", signature);
        
        // 构建请求URL
        HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + "/api/v1/usedcar/search").newBuilder();
        for (Map.Entry<String, Object> param : requestParams.entrySet()) {
            urlBuilder.addQueryParameter(param.getKey(), param.getValue().toString());
        }
        
        // 发送请求
        Request request = new Request.Builder()
                .url(urlBuilder.build())
                .addHeader("Authorization", "Bearer " + token)
                .addHeader("User-Agent", "AutoHome-Java-Client/1.0")
                .build();
        
        try (Response response = httpClient.newCall(request).execute()) {
            if (response.isSuccessful()) {
                String responseBody = response.body().string();
                Map<String, Object> result = objectMapper.readValue(responseBody, Map.class);
                return parseSearchResult(result);
            } else {
                throw new IOException("请求失败: " + response.code());
            }
        }
    }
    
    // 省略其他方法...
}

class UsedCarSearchParams {
    private Integer provinceId;
    private Integer cityId;
    private Integer districtId;
    private Integer brandId;
    private Integer seriesId;
    private Double minPrice;
    private Double maxPrice;
    private Double minMileage;
    private Double maxMileage;
    private Integer minYear;
    private Integer maxYear;
    private String fuelType;
    private String transmission;
    private String bodyType;
    private String keyword;
    private String sortField = "publish_time";
    private String sortOrder = "desc";
    private Integer pageNo = 1;
    private Integer pageSize = 20;
    
    // 省略getter/setter方法
}

4.3 PHP实现

php 复制代码
<?php
class AutoHomeUsedCarService
{
    private $appKey;
    private $appSecret;
    private $baseUrl;
    private $accessToken;
    private $tokenExpires;
    # 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex 
    public function __construct($appKey, $appSecret, $sandbox = true)
    {
        $this->appKey = $appKey;
        $this->appSecret = $appSecret;
        $this->baseUrl = $sandbox 
            ? 'https://sandbox-openapi.autohome.com.cn'
            : 'https://openapi.autohome.com.cn';
    }
    
    public function searchUsedCars($params = [])
    {
        // 获取访问令牌
        $token = $this->getAccessToken();
        
        // 构建请求参数
        $requestParams = [
            'app_key' => $this->appKey,
            'timestamp' => time(),
            'format' => 'json',
            'version' => '1.0'
        ];
        
        // 合并搜索参数
        $requestParams = array_merge($requestParams, $params);
        
        // 生成签名
        $signature = $this->generateSignature($requestParams);
        $requestParams['sign'] = $signature;
        
        // 发送请求
        $url = $this->baseUrl . '/api/v1/usedcar/search?' . http_build_query($requestParams);
        
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'User-Agent: AutoHome-PHP-Client/1.0'
            ]
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode === 200) {
            return json_decode($response, true);
        } else {
            throw new Exception("请求失败: HTTP {$httpCode}");
        }
    }
    
    private function getAccessToken()
    {
        // 检查token是否有效
        if ($this->accessToken && $this->tokenExpires && $this->tokenExpires > time()) {
            return $this->accessToken;
        }
        
        // 获取新token
        $timestamp = time();
        $params = [
            'app_key' => $this->appKey,
            'timestamp' => $timestamp,
            'grant_type' => 'client_credentials'
        ];
        
        $signature = $this->generateSignature($params);
        $params['sign'] = $signature;
        
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $this->baseUrl . '/oauth/token',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => http_build_query($params),
            CURLOPT_TIMEOUT => 30
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode === 200) {
            $result = json_decode($response, true);
            $this->accessToken = $result['access_token'];
            $this->tokenExpires = time() + $result['expires_in'] - 300; // 提前5分钟过期
            return $this->accessToken;
        } else {
            throw new Exception("获取token失败: HTTP {$httpCode}");
        }
    }
    
    private function generateSignature($params)
    {
        // 移除sign参数并排序
        unset($params['sign']);
        ksort($params);
        
        // 拼接参数字符串
        $paramStr = '';
        foreach ($params as $key => $value) {
            $paramStr .= $key . '=' . $value . '&';
        }
        $paramStr = rtrim($paramStr, '&');
        
        // 构建签名字符串
        $signStr = $this->appKey . $paramStr . $params['timestamp'] . $this->appSecret;
        
        // 计算HMAC-SHA256
        return hash_hmac('sha256', $signStr, $this->appSecret);
    }
}
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex 
// 使用示例
try {
    $service = new AutoHomeUsedCarService('your_app_key', 'your_app_secret');
    
    $result = $service->searchUsedCars([
        'province_id' => 11,
        'city_id' => 1101,
        'min_price' => 10,
        'max_price' => 30,
        'min_year' => 2018,
        'page_size' => 10
    ]);
    
    if ($result['success']) {
        foreach ($result['data']['list'] as $car) {
            echo "{$car['brand']['name']} {$car['series']['name']} - {$car['price']}万\n";
        }
    }
} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}
?>

五、返回结果解析

5.1 成功响应示例

复制代码
{
  "success": true,
  "code": 200,
  "message": "成功",
  "data": {
    "list": [
      {
        "id": 12345,
        "title": "2020款 奥迪A6L 45 TFSI 臻选致雅型",
        "price": 28.5,
        "original_price": 45.8,
        "mileage": 5.2,
        "year": 2020,
        "month": 6,
        "city": "北京市",
        "district": "朝阳区",
        "brand": {
          "id": 1,
          "name": "奥迪"
        },
        "series": {
          "id": 10,
          "name": "A6L"
        },
        "model": "45 TFSI 臻选致雅型",
        "fuel_type": "汽油",
        "transmission": "自动",
        "body_type": "轿车",
        "publish_time": "2026-01-15 14:30:00",
        "thumbnail_url": "https://img.autohome.com.cn/usedcar/12345.jpg",
        "source_type": "商家"
      }
    ],
    "pagination": {
      "page_no": 1,
      "page_size": 20,
      "total_count": 125,
      "total_pages": 7,
      "has_next": true,
      "has_previous": false
    },
    "summary": {
      "avg_price": 25.8,
      "avg_mileage": 6.5,
      "avg_year": 2019.5,
      "brand_distribution": {
        "奥迪": 15,
        "宝马": 12,
        "奔驰": 10
      }
    }
  }
}

5.2 错误响应示例

复制代码
{
  "success": false,
  "code": 400,
  "message": "参数错误:province_id无效",
  "data": null
}

5.3 状态码说明

状态码 说明 处理建议
200 成功 -
400 参数错误 检查请求参数格式
401 认证失败 检查API密钥和签名
403 权限不足 检查API权限范围
404 数据不存在 检查搜索条件
429 请求频率超限 降低请求频率
500 服务器错误 稍后重试

六、高级功能实现

6.1 智能搜索建议

复制代码
class IntelligentUsedCarSearch:
    """智能二手车搜索服务"""
    
    def __init__(self, api_client):
        self.client = api_client
        self.search_history = []
    
    def smart_search(self, query: str, location: str = None) -> SearchResult:
        """
        智能搜索:自动识别搜索类型
        
        Args:
            query: 搜索查询字符串
            location: 地区信息
        
        Returns:
            搜索结果
        """
        # 解析查询类型
        search_type = self._detect_search_type(query)
        
        # 构建搜索参数
        params = self._build_search_params(query, search_type, location)
        
        # 执行搜索
        result = self.client.search_used_cars(**params)
        
        # 记录搜索历史
        self._record_search_history(query, search_type, result)
        
        return result
    
    def _detect_search_type(self, query: str) -> str:
        """自动识别搜索类型"""
        import re
        
        # 检查是否为价格范围
        if re.match(r'^\d+-\d+万$', query):
            return 'price_range'
        
        # 检查是否为年份范围
        if re.match(r'^\d+-\d+年$', query):
            return 'year_range'
        
        # 检查是否为里程范围
        if re.match(r'^\d+-\d+万公里$', query):
            return 'mileage_range'
        
        # 检查是否为品牌+车系
        brand_patterns = ['奥迪', '宝马', '奔驰', '大众', '丰田', '本田']
        for brand in brand_patterns:
            if brand in query:
                return 'brand_model'
        
        # 默认为关键词搜索
        return 'keyword'
    
    def _build_search_params(self, query: str, search_type: str, location: str) -> Dict[str, Any]:
        """根据搜索类型构建参数"""
        params = {}
        import re
        
        if search_type == 'price_range':
            # 解析价格范围
            match = re.match(r'^(\d+)-(\d+)万$', query)
            if match:
                params['min_price'] = float(match.group(1))
                params['max_price'] = float(match.group(2))
        
        elif search_type == 'year_range':
            # 解析年份范围
            match = re.match(r'^(\d+)-(\d+)年$', query)
            if match:
                params['min_year'] = int(match.group(1))
                params['max_year'] = int(match.group(2))
        
        elif search_type == 'mileage_range':
            # 解析里程范围
            match = re.match(r'^(\d+)-(\d+)万公里$', query)
            if match:
                params['min_mileage'] = float(match.group(1))
                params['max_mileage'] = float(match.group(2))
        
        elif search_type == 'brand_model':
            # 解析品牌和车型
            params['keyword'] = query
        
        else:
            params['keyword'] = query
        
        # 添加地区信息
        if location:
            # 这里可以集成地理编码服务将地址转换为ID
            pass
        
        return params

6.2 数据缓存优化

复制代码
import redis
from functools import lru_cache

class CachedUsedCarAPI(AutoHomeUsedCarAPI):
    """带缓存的二手车API"""
    
    def __init__(self, app_key, app_secret, redis_client, sandbox=True):
        super().__init__(app_key, app_secret, sandbox)
        self.redis = redis_client
        self.cache_prefix = "autohome:usedcar:"
    
    def search_used_cars_cached(self, cache_key: str, **params) -> SearchResult:
        """
        带缓存的二手车搜索
        """
        # 检查缓存
        cached = self.redis.get(cache_key)
        if cached:
            data = json.loads(cached)
            return SearchResult(**data)
        
        # 调用API
        result = super().search_used_cars(**params)
        
        # 缓存结果
        if result.success:
            # 根据数据量设置缓存时间
            ttl = self._calculate_ttl(result)
            self.redis.setex(
                cache_key,
                ttl,
                json.dumps(result.__dict__)
            )
        
        return result
    
    def _calculate_ttl(self, result: SearchResult) -> int:
        """根据搜索结果计算缓存时间"""
        total_count = result.pagination.get('total_count', 0)
        
        if total_count == 0:
            return 1800  # 30分钟
        elif total_count <= 100:
            return 3600  # 1小时
        else:
            return 7200  # 2小时
    
    def get_cache_key(self, **params) -> str:
        """生成缓存键"""
        # 移除分页参数
        cache_params = params.copy()
        cache_params.pop('page_no', None)
        cache_params.pop('page_size', None)
        
        # 生成唯一键
        param_str = json.dumps(cache_params, sort_keys=True)
        return f"{self.cache_prefix}{hashlib.md5(param_str.encode()).hexdigest()}"

6.3 批量处理优化

复制代码
from concurrent.futures import ThreadPoolExecutor, as_completed

class BatchUsedCarProcessor:
    """批量二手车处理器"""
    
    def __init__(self, api_client):
        self.client = api_client
    
    def batch_search_by_regions(
        self,
        regions: List[Dict[str, int]],
        search_params: Dict[str, Any],
        max_workers: int = 3
    ) -> Dict[str, List[UsedCarBasicInfo]]:
        """
        按地区批量搜索二手车
        
        Args:
            regions: 地区列表 [{"province_id": 11, "city_id": 1101}, ...]
            search_params: 搜索参数
            max_workers: 最大并发数
        
        Returns:
            按地区分组的搜索结果
        """
        results = {}
        
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            # 提交所有地区搜索任务
            future_to_region = {
                executor.submit(
                    self.client.search_all_used_cars,
                    **{**search_params, **region}
                ): region
                for region in regions
            }
            
            # 收集结果
            for future in as_completed(future_to_region):
                region = future_to_region[future]
                try:
                    region_cars = future.result(timeout=300)  # 5分钟超时
                    region_key = f"{region.get('province_id')}_{region.get('city_id')}"
                    results[region_key] = region_cars
                except Exception as e:
                    print(f"地区 {region} 搜索失败: {e}")
        
        return results
    
    def analyze_regional_prices(
        self,
        regional_results: Dict[str, List[UsedCarBasicInfo]]
    ) -> Dict[str, Dict[str, float]]:
        """
        分析地区价格差异
        """
        analysis = {}
        
        for region_key, cars in regional_results.items():
            if not cars:
                continue
            
            prices = [car.price for car in cars if car.price > 0]
            mileages = [car.mileage for car in cars if car.mileage > 0]
            years = [car.year for car in cars if car.year > 0]
            
            analysis[region_key] = {
                'avg_price': sum(prices) / len(prices) if prices else 0,
                'avg_mileage': sum(mileages) / len(mileages) if mileages else 0,
                'avg_year': sum(years) / len(years) if years else 0,
                'car_count': len(cars)
            }
        
        return analysis

七、实战应用场景

7.1 二手车比价平台

复制代码
class UsedCarPriceComparator:
    """二手车比价平台"""
    
    def __init__(self, api_client):
        self.client = api_client
        self.price_history = {}
    
    def compare_prices_by_model(
        self,
        brand: str,
        model: str,
        regions: List[Dict[str, int]],
        year_range: Tuple[int, int] = (2018, 2023)
    ) -> Dict[str, Any]:
        """
        按车型比较地区价格差异
        """
        # 搜索各地区的同款车型
        search_params = {
            'min_year': year_range[0],
            'max_year': year_range[1],
            'keyword': f"{brand} {model}"
        }
        
        regional_results = self.client.batch_search_by_regions(regions, search_params)
        
        # 分析价格差异
        price_analysis = self.client.analyze_regional_prices(regional_results)
        
        # 生成价格对比报告
        report = {
            'brand': brand,
            'model': model,
            'year_range': year_range,
            'regional_prices': price_analysis,
            'recommendation': self._generate_recommendation(price_analysis)
        }
        
        return report
    
    def track_price_trend(
        self,
        car_id: int,
        duration_days: int = 30
    ) -> List[Dict[str, Any]]:
        """
        跟踪二手车价格趋势
        """
        price_history = []
        
        # 模拟获取历史价格数据
        for i in range(duration_days, 0, -1):
            # 实际应用中可以从数据库获取历史数据
            # 这里简化实现
            price_point = {
                'date': (datetime.now() - timedelta(days=i)).strftime('%Y-%m-%d'),
                'price': 25.5 + random.uniform(-1, 1),  # 模拟价格波动
                'market_avg': 26.0 + random.uniform(-0.5, 0.5)
            }
            price_history.append(price_point)
        
        return price_history

7.2 二手车推荐系统

复制代码
class UsedCarRecommender:
    """二手车推荐系统"""
    
    def __init__(self, api_client):
        self.client = api_client
    
    def recommend_cars_by_budget(
        self,
        budget: float,
        user_preferences: Dict[str, Any],
        region: Dict[str, int]
    ) -> List[UsedCarBasicInfo]:
        """
        根据预算推荐二手车
        """
        # 构建搜索参数
        search_params = {
            'province_id': region.get('province_id'),
            'city_id': region.get('city_id'),
            'min_price': max(1, budget * 0.7),  # 预算的70%作为最低价
            'max_price': budget * 1.1,  # 预算的110%作为最高价
            'min_year': user_preferences.get('min_year', 2018),
            'max_mileage': user_preferences.get('max_mileage', 15),
            'fuel_type': user_preferences.get('fuel_type'),
            'body_type': user_preferences.get('body_type'),
            'sort_field': 'publish_time',
            'sort_order': 'desc'
        }
        
        # 执行搜索
        result = self.client.search_used_cars(**search_params)
        
        if not result.success:
            return []
        
        cars = result.used_cars
        
        # 应用推荐算法
        recommended_cars = self._apply_recommendation_algorithm(cars, user_preferences)
        
        return recommended_cars[:10]  # 返回前10个推荐
    
    def _apply_recommendation_algorithm(
        self,
        cars: List[UsedCarBasicInfo],
        preferences: Dict[str, Any]
    ) -> List[UsedCarBasicInfo]:
        """应用推荐算法"""
        scored_cars = []
        
        for car in cars:
            score = 0
            
            # 价格得分(越接近预算上限得分越高)
            target_price = preferences.get('target_price', car.price)
            price_diff = abs(car.price - target_price)
            price_score = max(0, 100 - price_diff * 10)
            score += price_score * 0.3
            
            # 车龄得分
            current_year = datetime.now().year
            car_age = current_year - car.year
            age_score = max(0, 100 - car_age * 5)
            score += age_score * 0.25
            
            # 里程得分
            mileage_score = max(0, 100 - car.mileage * 2)
            score += mileage_score * 0.2
            
            # 品牌偏好得分
            preferred_brands = preferences.get('preferred_brands', [])
            if car.brand in preferred_brands:
                score += 50
            
            # 发布时间得分(越新越好)
            publish_time = datetime.fromisoformat(car.publish_time.replace(' ', 'T'))
            days_ago = (datetime.now() - publish_time).days
            recency_score = max(0, 100 - days_ago)
            score += recency_score * 0.1
            
            scored_cars.append((car, score))
        
        # 按得分排序
        scored_cars.sort(key=lambda x: x[1], reverse=True)
        
        return [car for car, score in scored_cars]

八、故障排查与优化

8.1 常见问题解决

问题1:签名验证失败
复制代码
def debug_signature_generation(params, app_secret, timestamp):
    """调试签名生成过程"""
    print("=== 签名调试信息 ===")
    
    # 排序参数
    sorted_params = sorted(params.items())
    param_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
    print(f"参数字符串: {param_str}")
    
    # 构建签名字符串
    sign_str = f"{app_key}{param_str}{timestamp}{app_secret}"
    print(f"签名字符串: {sign_str}")
    
    # 计算签名
    import hmac
    signature = hmac.new(
        app_secret.encode('utf-8'),
        sign_str.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    print(f"计算签名: {signature}")
    
    return signature
问题2:分页数据异常
复制代码
def stable_pagination_search(self, **params):
    """稳定的分页搜索"""
    # 确保有排序字段
    if 'sort_field' not in params:
        params['sort_field'] = 'id'  # 使用唯一字段排序
    
    if 'sort_order' not in params:
        params['sort_order'] = 'desc'
    
    # 使用游标分页
    all_cars = []
    last_id = None
    
    while True:
        if last_id:
            # 使用游标进行分页
            params['cursor'] = last_id
        
        result = self.search_used_cars(**params)
        
        if not result.success or not result.used_cars:
            break
        
        all_cars.extend(result.used_cars)
        last_id = result.used_cars[-1].car_id
        
        # 检查是否还有更多数据
        pagination = result.pagination
        if not pagination.get('has_next', False):
            break
        
        # 避免频繁请求
        time.sleep(0.5)
    
    return all_cars

8.2 性能优化建议

  1. 合理使用缓存

    多级缓存策略

    class MultiLevelCache:
    def init(self, redis_client):
    self.memory_cache = {}
    self.redis = redis_client
    self.memory_ttl = 300 # 5分钟
    self.redis_ttl = 3600 # 1小时

    复制代码
     def get_used_car_data(self, cache_key):
         # 1. 检查内存缓存
         if cache_key in self.memory_cache:
             data, expire_time = self.memory_cache[cache_key]
             if time.time() < expire_time:
                 return data
         
         # 2. 检查Redis缓存
         if self.redis:
             cached = self.redis.get(cache_key)
             if cached:
                 data = json.loads(cached)
                 # 更新内存缓存
                 self.memory_cache[cache_key] = (data, time.time() + self.memory_ttl)
                 return data
         
         return None
  2. 批量请求优化

    import asyncio
    import aiohttp

    async def batch_search_async(api_client, search_queries):
    """异步批量搜索"""
    async with aiohttp.ClientSession() as session:
    tasks = []
    for query in search_queries:
    task = api_client.search_used_cars_async(session, **query)
    tasks.append(task)

    复制代码
         results = await asyncio.gather(*tasks, return_exceptions=True)
         return results

九、最佳实践总结

9.1 安全实践

  1. 密钥管理:使用环境变量存储API密钥

  2. HTTPS强制:确保所有请求使用HTTPS

  3. 输入验证:验证所有输入参数

  4. 错误处理:不暴露敏感错误信息

9.2 性能实践

  1. 缓存策略:根据数据更新频率设置合适的缓存时间

  2. 批量操作:合并多个请求减少API调用次数

  3. 分页优化:使用游标分页提高稳定性

  4. 异步处理:批量操作使用异步方式提高吞吐量

9.3 业务实践

  1. 地区数据管理:维护地区ID映射表

  2. 品牌车系管理:缓存品牌车系信息

  3. 价格趋势分析:记录历史价格数据

  4. 推荐算法优化:基于用户行为优化推荐


附录:快速开始模板

复制代码
# quick_start.py
from autohome_usedcar import AutoHomeUsedCarAPI

# 1. 初始化客户端
client = AutoHomeUsedCarAPI(
    app_key="your_app_key",
    app_secret="your_app_secret",
    sandbox=True
)

# 2. 简单搜索
result = client.search_used_cars(
    province_id=11,  # 北京
    min_price=10,
    max_price=30,
    min_year=2018
)

if result.success:
    for car in result.used_cars:
        print(f"{car.brand} {car.series} - {car.price}万")

# 3. 批量获取
all_cars = client.search_all_used_cars(
    province_id=11,
    min_price=15,
    max_price=25
)
print(f"共找到 {len(all_cars)} 辆二手车")

# 4. 获取详情
car_detail = client.get_used_car_detail(12345)
if car_detail:
    print(f"车辆详情: {car_detail.basic_info.title}")

通过本攻略,您应该能够:

  • 理解汽车之家二手车搜索接口的完整功能

  • 实现安全的API认证和请求

  • 处理各种搜索条件和分页逻辑

  • 构建高性能的二手车搜索应用

  • 在实际业务中灵活应用该接口

建议根据实际业务需求选择合适的实现方案,并遵循最佳实践确保系统的稳定性和可维护性。

相关推荐
银发控、9 小时前
MySQL联合索引
数据库·mysql
予枫的编程笔记9 小时前
【MySQL修炼篇】从踩坑到精通:事务隔离级别的3大异常(脏读/幻读/不可重复读)解决方案
数据库·mysql·后端开发·数据库事务·事务隔离级别·rr级别·脏读幻读不可重复读
一起养小猫11 小时前
Flutter for OpenHarmony 实战:记账应用数据统计与可视化
开发语言·jvm·数据库·flutter·信息可视化·harmonyos
世界尽头与你11 小时前
(修复方案)CVE-2023-22047: Oracle PeopleSoft Enterprise PeopleTools 未授权访问漏洞
数据库·安全·oracle·渗透测试
韩立学长11 小时前
【开题答辩实录分享】以《智能大学宿舍管理系统的设计与实现》为例进行选题答辩实录分享
数据库·spring boot·后端
Henry Zhu12311 小时前
数据库(五):反规范化
数据库
Mr_Xuhhh12 小时前
MySQL函数详解:日期、字符串、数学及其他常用函数
java·数据库·sql
he___H12 小时前
Redis高级数据类型
数据库·redis·缓存
霖霖总总13 小时前
[小技巧60]深入解析 MySQL Online DDL:MySQL Online DDL、pt-osc 与 gh-ost 机制与最佳实践
数据库·mysql