二手车车源信息查询 API 实战:车型、图片、里程、首付与标签批量拉取
做二手车选品、车源同步、行情分析时,第一步往往卡在「没有数据」:公开平台的车源列表是动态渲染的,车型名称、图片、里程、首付、标签混在一堆 HTML 里,自己抓要处理分页、懒加载、反爬。本文介绍一个二手车车源信息查询接口:一次 GET 请求返回当前可查的车源列表,车型名称、图片链接、行驶里程、注册年份、首付价格、车辆标签一次拿全,且无车源返回时不收费。
api.xujian.tech- V
xujian_cq
一、车源数据显示场常见的几个坑
| 难点 | 具体表现 |
|---|---|
| 数据是渲染出来的 | 车源列表走 JS 动态加载,HTML 里只有空壳,直接抓页面拿不到数据 |
| 分页与懒加载 | 图片滚动到底才加载,分页接口往往带签名,逆向成本很高 |
| 字段不规范 | 车型名称是「长安启源E07 2025款 纯电 两驱 90kWh Max智驾版」这种长文本,年款、配置、续航都挤在一起 |
| 里程单位混乱 | 有的写「3.2万公里」,有的写「32000」,有的写「3.2」无单位 |
| 首付价格口径不一 | 「首付 3.8万」可能是月供,也可能是首付总额 |
| 数据量不可控 | 一次拉回上千条,存储和渲染都吃不消 |
把这一步收敛成一个接口,价值在于:字段已经清洗好(里程统一为万公里数值文本、首付统一为万元、regDate统一为「年份」文本、标签已经是数组),并且可以用limit控制返回条数。
二、接口能力概览
2.1 接口基础信息
| 项目 | 说明 |
|---|---|
| 接口地址 | https://api.xujian.tech/openapi/usedcar/list |
| 接口编码 | usedcar.list |
| 请求方式 | GET(limit放 Query String) |
| 鉴权方式 | 请求头X-API-Key,不做签名、时间戳或加密 |
| 返回格式 | JSON,Content-Type: application/json;charset=UTF-8 |
| 单次费用 | 0.01 元/次 |
| 最多返回 | 50 条(limit缺省即上限) |
| 是否需要业务入参 | 否,只有可选的limit |
| 典型耗时 | 数百毫秒 ~ 数秒(响应体costMs字段为本次真实耗时) |
| 在线文档 | https://api.xujian.tech/api/usedcar-list |
2.2 请求参数
请求头:
| 参数名 | 必填 | 说明 |
|---|---|---|
X-API-Key | 是 | 开发者 API Key,缺失或无效直接返回失败 |
业务参数:
| 参数名 | 必填 | 类型 | 示例 | 说明 |
|---|---|---|---|---|
limit | 否 | Integer | 20 | 期望返回条数;取值范围 1 ~ 50,缺省按上限 50 返回;传0或负数按上限处理 |
这个接口没有筛选入参(品牌 / 价格 / 里程等筛选需要在调用侧自己做)。设计上它是「一次拉全量、本地再筛选」的模式:单次最多 50 条,本地过滤远比在接口上堆一堆分不清的筛选参数省心。
2.3 计费上比较实在的一点
接口是先预鉴权、查到结果后再扣费的两段式流程。下面这些情况直接返回失败,不扣费、不写扣费流水、不累加调用次数:
- 上游数据服务暂时不可用(超时或网络异常);
- 当前没有可查车源(上游返回空列表)。
也就是说,只有真正返回了至少一条车源才计一次费用。做定时任务同步时,服务异常或空结果不会白白吃掉预算。
三、返回字段详解
3.1 顶层字段
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 0成功,非 0 失败(常见为500) |
msg | String | 结果描述,成功为success,失败为具体原因 |
data | Object | 业务数据,失败时为null |
3.2 data 字段
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
total | int | 20 | 本次实际返回的车源条数 |
list | Array | […] | 车源列表 |
apiCode | String | usedcar.list | 接口编码 |
apiName | String | 二手车信息查询 | 接口名称 |
chargeType | String | PER_CALL | 计费类型 |
balance | BigDecimal | 99.9800 | 调用完成后(已扣费)的账户余额(元) |
costMs | Long | 1120 | 本次调用耗时(毫秒) |
注意:本接口返回的
data不含keyword(没有业务入参),与企业系列接口的返回结构略有差异,解析时别写死取keyword。
3.3 list[] 车源对象字段
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
carName | String | 长安启源E07 2025款 纯电 两驱 90kWh Max智驾版 | 车型名称(含品牌、年款、配置版本) |
imageUrl | String | https://…/car.jpg | 车辆图片链接,可直接用于<img src> |
mileage | String | 3.20 | 行驶里程,单位:万公里(纯数值文本) |
regDate | String | 2024年 | 车辆注册年份 |
downPayment | String | 3.80 | 首付价格,单位:万元(纯数值文本) |
tags | Array | [“新上架”,“准新车”,“0次过户”] | 车辆标签数组 |
常见的tags取值:新上架 / 准新车 / 0次过户 / 原厂质保 / 个人一手 / 支持分期 / 7天无理由退车 等,属于平台运营标签,会随车源动态变化,业务侧建议做白名单翻译而非硬编码全部枚举。
两个字段设计上的细节:一是
mileage/downPayment是不带单位的数值文本(3.20表示 3.2 万公里、3.80表示 3.8 万元),拼接展示时自己补单位;二是上游的内部数据主键(dataId/dId/cid)不对外返回,需要唯一标识时建议使用carName+regDate+mileage组合键。
四、调用示例
4.1 curl
# 拉满 50 条curl-s-G"https://api.xujian.tech/openapi/usedcar/list"\-H"X-API-Key: 你的APIKey"# 只要 20 条curl-s-G"https://api.xujian.tech/openapi/usedcar/list"\--data-urlencode"limit=20"\-H"X-API-Key: 你的APIKey"4.2 Java(Hutool)
importcn.hutool.http.HttpRequest;importcn.hutool.json.JSONArray;importcn.hutool.json.JSONObject;importcn.hutool.json.JSONUtil;publicclassUsedCarListClient{privatestaticfinalStringAPI_URL="https://api.xujian.tech/openapi/usedcar/list";/** * 查询二手车车源列表 * * @param apiKey 开发者 API Key * @param limit 期望返回条数,1 ~ 50;传 null 取上限 * @return 车源列表;无车源或查询失败返回 null,且不扣费 */publicstaticJSONArraylist(StringapiKey,Integerlimit){HttpRequestreq=HttpRequest.get(API_URL).header("X-API-Key",apiKey).timeout(20000);if(limit!=null&&limit>0){req.form("limit",limit);}JSONObjectjson=JSONUtil.parseObj(req.execute().body());Integercode=json.getInt("code");if(code==null||code!=0){System.out.println("查询失败(不收费):"+json.getStr("msg"));returnnull;}returnjson.getJSONObject("data").getJSONArray("list");}publicstaticvoidmain(String[]args){JSONArraylist=list("你的APIKey",20);if(list==null){return;}for(inti=0;i<list.size();i++){JSONObjectc=list.getJSONObject(i);System.out.printf("%s | %s万公里 | %s | 首付 %s万%n",c.getStr("carName"),c.getStr("mileage"),c.getStr("regDate"),c.getStr("downPayment"));}}}4.3 Python
importrequestsdefusedcar_list(api_key:str,limit:int=50):""" 查询二手车车源列表 Args: api_key: 开发者 API Key limit: 期望返回条数,1 ~ 50 Returns: list: 成功返回车源列表;无车源或失败返回 None,且不扣费 """resp=requests.get("https://api.xujian.tech/openapi/usedcar/list",params={"limit":limit},headers={"X-API-Key":api_key},timeout=20,)result=resp.json()ifresult.get("code")!=0:print("查询失败(不收费):",result.get("msg"))returnNonereturnresult["data"]["list"]if__name__=="__main__":forcarinusedcar_list("你的APIKey",20)or[]:print(car["carName"],car["mileage"],car["regDate"],car["tags"])4.4 JavaScript(浏览器 / Node 18+)
constresp=awaitfetch("https://api.xujian.tech/openapi/usedcar/list?limit=20",{headers:{"X-API-Key":API_KEY},});const{code,msg,data}=awaitresp.json();if(code===0){data.list.forEach((car)=>{console.log(car.carName,`${car.mileage}万公里`,car.tags.join("/"));});}else{console.warn("查询失败(不收费):",msg);}五、返回示例
5.1 成功返回
{"code":0,"msg":"success","data":{"total":3,"list":[{"carName":"长安启源E07 2025款 纯电 两驱 90kWh Max智驾版","imageUrl":"https://example.com/car/e07-1.jpg","mileage":"0.50","regDate":"2025年","downPayment":"5.60","tags":["新上架","准新车","0次过户","原厂质保"]},{"carName":"大众迈腾 2021款 330TSI DSG 豪华型","imageUrl":"https://example.com/car/magotan-1.jpg","mileage":"3.20","regDate":"2021年","downPayment":"3.80","tags":["个人一手","支持分期"]},{"carName":"丰田凯美瑞 2019款 2.5G 豪华版","imageUrl":"https://example.com/car/camry-1.jpg","mileage":"6.80","regDate":"2019年","downPayment":"2.90","tags":["7天无理由退车"]}],"apiCode":"usedcar.list","apiName":"二手车信息查询","chargeType":"PER_CALL","balance":99.9800,"costMs":1120}}5.2 无可用车源(不收费)
{"code":500,"msg":"未查询到可用车源,请稍后重试;本次调用不计费","data":null}六、典型应用场景
6.1 车源同步落库(含去重键)
由于没有返回业务主键,落库时用组合键去重:
defsync_cars(api_key:str,db:dict,limit:int=50)->int:"""把车源同步到本地字典,返回新增条数"""cars=usedcar_list(api_key,limit)or[]added=0forcarincars:# 车型 + 年份 + 里程 组合作为去重键key=f"{car['carName']}|{car['regDate']}|{car['mileage']}"ifkeynotindb:db[key]=car added+=1returnadded图片链接建议不要长期外链:第三方图床随时可能失效或加防盗链,同步时把图片下载到自己的对象存储更稳。
6.2 本地筛选(替代接口筛选参数)
接口没提供筛选入参,但总数据量最多 50 条,本地过滤完全够:
functionpick(cars,{maxMileage,minYear,maxDownPayment,tags}){returncars.filter((c)=>{constyear=Number(String(c.regDate).replace("年",""));constmileage=Number(c.mileage);constpay=Number(c.downPayment);return((!maxMileage||mileage<=maxMileage)&&(!minYear||year>=minYear)&&(!maxDownPayment||pay<=maxDownPayment)&&(!tags||tags.some((t)=>c.tags.includes(t))));});}6.3 车型名称结构化
carName是长文本,展示前通常要拆出品牌、年款、配置三段:
privatestaticString[]splitCarName(StringcarName){// 长安启源E07 2025款 纯电 两驱 90kWh Max智驾版 →// [品牌+车型前缀, 2025款, 剩余配置描述]String[]parts=carName.split(" ",3);String[]arr=newString[3];arr[0]=parts.length>0?parts[0]:"";// 长安启源E07arr[1]=parts.length>1?parts[1]:"";// 2025款arr[2]=parts.length>2?parts[2]:"";// 纯电 两驱 90kWh Max智驾版returnarr;}品牌字典可以用这张拆分结果去建:把第一段与已知品牌表做前缀匹配即可。
6.4 行情统计(首付与里程分布)
fromcollectionsimportCounterdefstats(api_key:str,limit:int=50)->dict:"""按注册年份统计车源数量与平均首付"""cars=usedcar_list(api_key,limit)or[]by_year=Counter(c["regDate"]forcincars)pays=[float(c["downPayment"])forcincarsifc.get("downPayment")]return{"byYear":dict(by_year),"avgDownPayment":round(sum(pays)/len(pays),2)ifpayselse0,}七、提升可用性的几条实践建议
limit别贪心。上限 50 条,实际场景(选品抽查、行情抽样)用 20 ~ 30 条足够,还能省带宽与解析开销。- 数值字段已统一单位。
mileage万公里、downPayment万元,直接Float.parseFloat即可,不要再去解析单位文本。 - 图片尽快转存。第三方图片链接有防盗链与过期风险,展示型业务建议同步时转存到自己的 OSS。
- 标签做白名单翻译。
tags是运营标签、会动态增减,未知标签直接原样展示比写死枚举好。 - 定时任务做好幂等。没有业务主键,务必用组合键(
carName+regDate+mileage)去重。 - 区分「无车源」与「服务异常」。前者是
未查询到可用车源……本次调用不计费,后者是数据服务暂时不可用……,重试策略应不同。
八、错误码与排查
| code | msg(示例) | 处理建议 |
|---|---|---|
| 0 | success | 调用成功 |
| 500 | 缺少请求头 X-API-Key | 在请求头补充X-API-Key |
| 500 | API Key 无效 / API Key 已停用 | 检查 Key 是否正确,或在控制台重新启用 |
| 500 | 客户不存在或已停用 | 联系平台确认账号状态 |
| 500 | 接口不存在或已停用 | 确认usedcar.list当前是否维护中 |
| 500 | 余额不足,请先充值 | 按次计费接口调用前校验余额,余额不足不扣费,充值后重试 |
| 500 | 未查询到可用车源…… | 上游暂无车源,稍后重试;不计费 |
| 500 | 二手车服务未启用 / 二手车服务未配置(上游凭证缺失) | 平台侧配置问题,稍后重试;不计费 |
| 500 | 数据服务暂时不可用(请求上游超时或网络异常) | 稍后重试,不计费 |
九、计费与接入
| 项目 | 说明 |
|---|---|
| 单次费用 | 0.01 元/次 |
| 计费方式 | 按次计费,调用前校验余额,返回车源后才扣费 |
| 不计费场景 | 服务暂时不可用、上游返回空车源列表 |
| 返回条数 | 最多 50 条,用limit收敛 |
接入流程:注册开发者账号 → 控制台创建 API Key → 请求头带上X-API-Key即可调用,无需签名或加密。控制台可查看调用量、扣费流水与余额。
控制台地址:
https://api.xujian.tech;接口接入、数据与充值相关问题可联系微信xujian_cq。
十、总结
车源数据的难点从来不在算法,而在「拿到干净的结构化数据」这一层。这个接口的价值就是把渲染型页面里最难处理的部分——车型长文本、图片链接、里程与首付的单位统一、动态运营标签——提前处理好,调用方拿到 JSON 就能直接入库或渲染。
几个关键取舍值得留意:
- 无车源不收费:定时任务遇到空结果不会产生费用;
- 单位收敛到字段语义:
mileage万公里、downPayment万元,都是可直接 parse 的数值文本; - 不放一堆看不懂的筛选参数:总数据量上限 50 条,本地过滤比堆接口参数更灵活;
- 内部标识不外传:上游
dataId/cid等主键不返回,避免调用方依赖一个随时可能变的内部约定。