一、接口概览
1.1 接口简介
item_search接口是汽车之家开放平台的核心接口之一,专门用于二手车信息检索。支持按地区、品牌、价格、车龄等多维度筛选二手车,返回结构化的车辆列表信息。1.2 核心功能
- ✅ 地区筛选:按省市区精准定位二手车源
- ✅ 多条件搜索:品牌、车系、价格、里程、车龄等
- ✅ 分页查询:支持大数据量的分页加载
- ✅ 排序功能:按价格、里程、发布时间等排序
- ✅ 字段选择:可指定返回字段,优化网络传输
二、准备工作
2.1 环境配置
# requirements.txtrequests>=2.28.0python-dotenv>=1.0.0pydantic>=2.0.0aiohttp>=3.8.0redis>=4.5.0
2.2 认证配置
# config.pyimport osfrom 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完整实现
import requestsimport timeimport hashlibimport hmacimport jsonfrom typing import Dict, Any, List, Optionalfrom datetime import datetime, timedeltafrom dataclasses import dataclassfrom urllib.parse import urlencodeimport redis@dataclassclass 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 # 个人/商家@dataclassclass UsedCarDetail: """二手车详细信息"""
basic_info: UsedCarBasicInfo
images: List[str]
specs: Dict[str, str]
seller_info: Dict[str, Any]
inspection_report: Dict[str, Any]@dataclassclass 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: """
搜索二手车
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
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: 返回字段列表
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
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实现
import com.fasterxml.jackson.annotation.JsonInclude;import com.fasterxml.jackson.databind.ObjectMapper;import okhttp3.*;import org.slf4j.Logger;import org.slf4j.LoggerFactory;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实现
<?phpclass AutoHomeUsedCarService{ private $appKey; private $appSecret; private $baseUrl; private $accessToken; private $tokenExpires;
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();
// 构建请求参数
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
$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);
}
}// 使用示例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 params6.2 数据缓存优化
import redisfrom functools import lru_cacheclass 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_completedclass 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_history7.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_cars8.2 性能优化建议
- 合理使用缓存
# 多级缓存策略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- 批量请求优化
import asyncioimport aiohttpasync 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 安全实践
- 密钥管理:使用环境变量存储API密钥
- HTTPS强制:确保所有请求使用HTTPS
- 输入验证:验证所有输入参数
- 错误处理:不暴露敏感错误信息
9.2 性能实践
- 缓存策略:根据数据更新频率设置合适的缓存时间
- 批量操作:合并多个请求减少API调用次数
- 分页优化:使用游标分页提高稳定性
- 异步处理:批量操作使用异步方式提高吞吐量
9.3 业务实践
- 地区数据管理:维护地区ID映射表
- 品牌车系管理:缓存品牌车系信息
- 价格趋势分析:记录历史价格数据
- 推荐算法优化:基于用户行为优化推荐
附录:快速开始模板
# quick_start.pyfrom 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认证和请求
- 处理各种搜索条件和分页逻辑
- 构建高性能的二手车搜索应用
- 在实际业务中灵活应用该接口
建议根据实际业务需求选择合适的实现方案,并遵循最佳实践确保系统的稳定性和可维护性。