用招标网 API 获取项目详情,核心是先通过列表接口拿到项目唯一 ID,再用详情接口传入 ID 获取完整信息 。下面给你一套可直接落地的完整流程 + 代码示例 + 避坑要点(以主流招标网 / 标讯 API 为例,通用逻辑一致)。
一、整体流程(3 步搞定)
- 获取项目列表 :按关键词 / 地区 / 时间筛选,拿到每条公告的项目 ID / 公告 ID(唯一标识)。
- 调用详情接口:用上一步拿到的 ID,请求详情接口,获取完整结构化数据(标题、预算、采购人、中标人、附件、正文等)。
- 数据处理与存储:解析 JSON,去重、清洗、入库 / 导出。
二、关键前提
- 已在对应平台(招标网、APISpace、阿里云市场等)开通 API 权限 ,拿到:
- API Key / Token
- 接口地址(列表接口 + 详情接口)
- 字段说明文档
- 明确你要的字段:项目编号、标题、发布时间、预算金额、采购人、代理机构、中标人、中标金额、公告类型、附件链接、正文内容等。
三、通用接口参数说明(以 APISpace 标讯 API 为例)
1)列表查询接口(获取 ID)
- 请求方式:GET/POST
- 核心参数 :
keyword:关键词(如 "存储芯片""服务器""EPC")province:省份(如 "北京""广东")publishStartTime/publishEndTime:发布时间范围noticeType:公告类型(招标 / 中标 / 变更 / 废标等)pageNum/pageSize:分页
- 返回核心字段 :
id/noticeId/projectId:详情接口必须的唯一 IDtitle:标题publishTime:发布时间area:地区
2)详情查询接口(获取完整信息)
- 请求方式:GET/POST
- 核心参数 :
id/noticeId:从列表接口拿到的唯一 ID(必填)
- 返回核心字段 (结构化):
- 基础信息:项目编号、标题、公告类型、发布时间、截止时间
- 采购信息:采购人、代理机构、联系人、联系电话(部分脱敏)
- 预算与中标:预算金额、中标人、中标金额、中标日期
- 内容:公告正文(HTML / 纯文本)、附件下载链接
- 其他:行业分类、项目地址、监督部门等
四、Python 完整示例(可直接改参数跑)
以 APISpace 全国招投标查询 API 为例(其他平台逻辑一致,仅 URL、Header、参数名略有差异)。
步骤 1:安装依赖
bash
运行
pip install requests
步骤 2:完整代码(列表→详情→解析)
python
运行
import requests
import json
# ========== 1. 配置信息(替换成你自己的) ==========
API_KEY = "你的APISpace Token"
LIST_API_URL = "https://eolink.o.apispace.com/1063/bids/search" # 列表接口
DETAIL_API_URL = "https://eolink.o.apispace.com/1063/bids/detail" # 详情接口
HEADERS = {
"X-APISpace-Token": API_KEY,
"Content-Type": "application/json"
}
# ========== 2. 第一步:查询列表,获取项目ID ==========
def get_bid_list(keyword, province, start_time, end_time, page_size=10):
params = {
"keyword": keyword,
"province": province,
"publishStartTime": start_time,
"publishEndTime": end_time,
"pageSize": page_size,
"pageNum": 1
}
try:
resp = requests.get(LIST_API_URL, headers=HEADERS, params=params, timeout=15)
resp.raise_for_status()
data = resp.json()
# 打印列表结构,方便你看字段名
# print(json.dumps(data, ensure_ascii=False, indent=2))
return data.get("data", {}).get("list", [])
except Exception as e:
print(f"列表接口调用失败: {e}")
return []
# ========== 3. 第二步:根据ID获取详情 ==========
def get_bid_detail(notice_id):
params = {
"id": notice_id # 注意:不同平台参数名可能是 noticeId/projectId
}
try:
resp = requests.get(DETAIL_API_URL, headers=HEADERS, params=params, timeout=15)
resp.raise_for_status()
data = resp.json()
return data.get("data", {})
except Exception as e:
print(f"详情接口调用失败: {e}")
return None
# ========== 4. 主流程 ==========
if __name__ == "__main__":
# 1)查询条件(按你的业务改)
KEYWORD = "存储芯片"
PROVINCE = "全国"
START_TIME = "2026-01-01"
END_TIME = "2026-01-22"
# 2)获取列表
bid_list = get_bid_list(KEYWORD, PROVINCE, START_TIME, END_TIME, page_size=5)
if not bid_list:
print("未查询到招标信息")
exit()
# 3)遍历列表,逐个获取详情(示例取前2条)
for idx, bid in enumerate(bid_list[:2], 1):
notice_id = bid.get("id") # 关键:拿到ID
title = bid.get("title", "")
publish_time = bid.get("publishTime", "")
print(f"\n===== 第{idx}条:{title} =====")
print(f"发布时间: {publish_time}")
print(f"公告ID: {notice_id}")
# 4)获取详情
detail = get_bid_detail(notice_id)
if not detail:
continue
# 5)解析你需要的字段(按需提取)
project_code = detail.get("projectCode", "") # 项目编号
purchaser = detail.get("purchaser", "") # 采购人
agency = detail.get("agency", "") # 代理机构
budget = detail.get("budget", "") # 预算金额
winner = detail.get("winner", "") # 中标人
win_amount = detail.get("winAmount", "") # 中标金额
content = detail.get("content", "")[:300] + "..." # 正文预览
print(f"项目编号: {project_code}")
print(f"采购人: {purchaser}")
print(f"代理机构: {agency}")
print(f"预算金额: {budget}")
print(f"中标人: {winner}")
print(f"中标金额: {win_amount}")
print(f"正文预览: {content}")
五、不同平台的 "ID 字段名" 差异(常见坑)
很多人调用详情失败,就是ID 字段名不对,按你用的平台对应改:
| 平台 | 列表返回 ID 字段 | 详情接口参数名 |
|---|---|---|
| APISpace | id |
id |
| 招标网(hq.zhaobiao.cn) | noticeId/projectId |
noticeId/projectId |
| 中国采招网 | id |
id |
| 阿里云市场标讯 API | notice_id |
notice_id |
解决方法:先打印列表返回的完整 JSON,找到唯一标识字段,再填到详情接口。
六、必做优化与避坑(生产环境必备)
1)去重(避免重复调用详情)
-
用
noticeId/projectId做唯一键,存入数据库 / Redis / 本地文件,已获取过的不再请求。 -
示例(简单本地去重): python
运行
import os HISTORY_FILE = "bid_history.txt" def is_fetched(notice_id): if not os.path.exists(HISTORY_FILE): return False with open(HISTORY_FILE, "r", encoding="utf-8") as f: return notice_id in f.read().splitlines() def mark_fetched(notice_id): with open(HISTORY_FILE, "a", encoding="utf-8") as f: f.write(notice_id + "\n")调用时:
python
运行
if is_fetched(notice_id): continue detail = get_bid_detail(notice_id) mark_fetched(notice_id)
2)分页与并发控制
- 列表接口默认只返回一页,需循环
pageNum拉全量。 - 不要并发过高(QPS 5-10 以内),避免被限流 / 封号。
- 加延时 :
time.sleep(0.5)或time.sleep(1)。
3)异常处理
- 超时重试:
tenacity或简单for i in range(3)重试。 - 401/403:检查 API Key 是否过期、权限是否开通。
- 429:限流,降低调用频率或升级套餐。
4)数据清洗
- 金额:统一单位(元 / 万元),去除 "¥""万元" 等字符,转数字。
- 时间:统一格式
YYYY-MM-DD HH:MM:SS。 - 正文:HTML 转纯文本(用
BeautifulSoup)。
七、进阶:实时推送(替代轮询)
如果需要秒级 / 分钟级 获取新公告,优先用平台的WebHook / 消息推送:
- 在平台配置推送地址(你的服务接口)。
- 设置订阅条件(关键词、地区、行业)。
- 平台有新公告时,主动 POST 给你,包含
noticeId,你再调用详情接口。
- 优点:实时、省调用量、成本低。
- 缺点:需要部署公网可访问的接收服务。