拟在建项目详情接口用于根据项目id与发布时间publishTime取回项目标题、正文与建设单位。它通常作为“先搜索、再取详情”链路中的第二步:先用搜索接口拿到列表和每条记录的标识,再逐条拉取详情。本文从零开始,说明第一次调用需要准备什么、最小请求怎么发、返回值怎么看。
适用场景与能力边界
典型用途包括:把项目列表中的条目展开成完整正文做结构化入库;在业务系统中展示建设单位和项目概况;对搜索结果做二次筛选时,需要正文内容做关键词匹配。
接口返回的核心是三个业务字段:标题、正文、建设单位。它不做搜索、不做列表分页,也不提供项目的实时进度更新。换句话说,这个接口是“按标识取详情”,输入必须来自搜索结果的id与publishTime。
需要特别注意publishTime的作用:它是定位记录的必要条件之一,而不是可选装饰。只传id可能无法唯一定位或匹配失败,具体约束以文档为准。参数名支持publishTime与publishtime两种写法,大小写不敏感,接入时按代码风格选用即可,不必为此纠结。
调用前的准备
需要三样东西:一个从搜索结果中拿到的id、对应的publishTime,以及一个 API Key。
API Key 在平台控制台中复制。鉴权方式是固定的:在请求头中写Authorization: Bearer <你的Key>。这是该接口推荐的鉴权形式,不要依赖 Query 参数传 Key。
请求方法为POST,地址为https://v1.apizero.cn/api/planned-detail,参数以 JSON 形式放在请求体中,并设置Content-Type: application/json。
最小请求:curl 示例
把YOUR_API_KEY替换为实际 Key,id与publishTime替换为搜索接口返回的对应值:
curl -X POST "https://v1.apizero.cn/api/planned-detail" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id": "123456", "publishTime": "2024-06-15"}'若代码中习惯全小写,也可写成:
curl -X POST "https://v1.apizero.cn/api/planned-detail" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id": "123456", "publishtime": "2024-06-15"}'publishTime的格式(日期字符串还是时间戳)应与搜索接口返回值保持一致,具体格式以文档为准。
Python 接入示例
下面封装一个函数,把搜索结果的单条记录直接映射成详情请求参数,避免手工拼字段时出错:
import os import requests def fetch_planned_detail(item: dict, api_key: str | None = None) -> dict: """item 为搜索接口返回的单条记录,需含 id 与 publishTime""" record_id = item.get("id") publish_time = item.get("publishTime") or item.get("publishtime") if not record_id or not publish_time: raise ValueError("记录缺少 id 或 publishTime,无法取详情") url = "https://v1.apizero.cn/api/planned-detail" body = {"id": record_id, "publishTime": publish_time} headers = {"Content-Type": "application/json"} key = api_key or os.environ.get("APIZERO_KEY") if key: headers["Authorization"] = f"Bearer {key}" resp = requests.post(url, json=body, headers=headers, timeout=20) resp.raise_for_status() payload = resp.json() if payload.get("code") != 0: raise RuntimeError(f"获取详情失败: code={payload.get('code')}, msg={payload.get('msg')}") return payload["data"] # 假设 search_items 来自搜索接口 # for item in search_items: # detail = fetch_planned_detail(item) # print(detail.get("title"), detail.get("builder"))把“取参”和“请求”拆开的写法在批量场景中很有用:可以在循环外统一处理异常,对单条失败做跳过与记录,而不是中断整批任务。
返回字段解读
成功响应的code == 0,data内保留业务字段,并额外附带fetched_at与api。字段含义如下(名称与结构以文档为准):
| 字段 | 含义 |
|---|---|
title | 项目标题 |
content | 项目正文 |
builder | 建设单位 |
fetched_at | 本次数据抓取时间 |
api | 数据来源标识 |
三个业务字段里,content通常是长文本,可能含换行与 HTML 片段,入库前应做清洗。builder是建设单位名称,可能存在多家单位并列或为空的情况,读取时做空值判断。fetched_at与api是元信息,用于标注数据新鲜度与来源,建议随业务字段一起存储,便于后续审计。
一个实践提醒:content的长度可能超出某些数据库字段的默认限制,建表时用TEXT或LONGTEXT,不要用VARCHAR(255)硬扛。
常见错误与排查
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
code非 0,提示参数缺失 | 未传publishTime或id为空 | 检查请求体两个字段是否都有值 |
code非 0,提示鉴权失败 | 未使用Authorization: Bearer或 Key 无效 | 按固定格式补请求头 |
code非 0,提示未找到记录 | id与publishTime不匹配 | 用搜索接口原始返回值重试 |
| HTTP 415 或不支持 | 缺少Content-Type: application/json | 补上请求头 |
| HTTP 429 | 超出调用频率 | 控制并发,QPS 以文档为准 |
第三行是接入初期最常见的问题:手工改写或截断了publishTime,导致与记录不匹配。务必原样透传搜索接口的返回值。
工程化注意事项
先搜索后详情,不要跳过链路。详情接口的入参完全依赖搜索结果的标识,单独构造id没有意义。把两步封装成一个业务方法,调用方只传查询条件即可。
批量拉取要做节流。对列表逐条取详情时,加固定间隔或使用带并发上限的线程池,避免瞬时打满限流。失败条目记录下来,稍后重试,不要在同一批里无限重试。
详情做持久化,不要每次现拉。项目详情内容变化频率低,取回后入库存储,fetched_at可作为是否需要更新的判断依据。这也减少了重复请求。
正文清洗单独抽函数。去 HTML 标签、压缩空白、统一编码,这些处理在多个数据源场景下都会复用,不要散落在业务流程里。
Key 只在服务端。通过后端代理调用,密钥不进入前端代码或移动端包体。
参考文档
- 接口文档
- 接口地址