何时使用
当你需要在 ClinicalTrials.gov(全球临床试验注册库)中检索、获取或导出试验数据时使用本技能。典型场景:
- 查找某疾病/适应症正在招募(RECRUITING)的临床试验
- 查找测试某药物 / 器械 / 干预手段的试验
- 按地理区域定位试验(患者转诊 / 就近入组)
- 跟踪某申办方或机构的临床试验组合(portfolio)
- 按 NCT 编号取单条试验的入排标准、结局指标、联系人详情
- 跨治疗领域分析试验趋势(阶段、入组人数、时间线)
- 为系统综述 / meta 分析批量导出试验数据,或监测状态变更与结果发布
不该用本技能的边界:
- 化合物生物活性 / IC50/Ki 等 SAR 数据 → 用
chembl-bioactivity-database - 一站式跨多个科研/生物医学库的统一 REST 查询 → 用
scientific-database-lookup - 发表文献检索(本库只覆盖试验注册,不含论文)→ 转科研文献检索类技能
步骤 / 指令
准备环境:
pip install requests(唯一必需,无需 API key);表格分析另装pandas。设定基址:
CT_API = "https://clinicaltrials.gov/api/v2"。约束:公开免鉴权;速率约 50 请求/分钟/IP;单页最大pageSize=1000;日期为 ISO 8601,文本字段为 CommonMark Markdown;响应格式json(默认)或csv。选端点:
GET /studies?<query 参数>—— 检索(返回totalCount、studies[]、nextPageToken)GET /studies/{NCT_ID}—— 取单条试验详情(如NCT04852770)
拼查询参数(
requests.get(..., params={...})自动 URL 编码):参数 含义 示例 query.cond疾病/适应症 lung cancerquery.intr干预/药物 Pembrolizumabquery.locn地理位置 New Yorkquery.spons申办方 National Cancer Institutequery.term全文检索 immunotherapyfilter.overallStatus状态(逗号分隔多值) RECRUITING,COMPLETEDfilter.phase阶段(逗号分隔) PHASE2,PHASE3filter.idsNCT 编号过滤 NCT04852770sort排序 LastUpdatePostDate:descpageSize每页条数(≤1000,默认 10) 100pageToken翻页游标 上次响应的 nextPageTokenformat响应格式 json/csv排序字段:
LastUpdatePostDate/EnrollmentCount/StartDate/StudyFirstPostDate,各带:asc或:desc。状态枚举(大小写敏感,须精确匹配):
RECRUITING(招募中)、NOT_YET_RECRUITING(获批未开)、ENROLLING_BY_INVITATION(邀请入组)、ACTIVE_NOT_RECRUITING(在研已截止入组)、SUSPENDED(暂停)、TERMINATED(提前终止)、COMPLETED(已完成)、WITHDRAWN(入组前撤回)。阶段枚举:
EARLY_PHASE1、PHASE1(安全/剂量)、PHASE2(有效性/副作用)、PHASE3(大规模有效性)、PHASE4(上市后监测)、NA(非药物研究不适用)。取嵌套字段(响应为深层嵌套 JSON,统一从
study['protocolSection']进入;缺模块用.get()或safe_get兜底):数据 路径 NCT ID protocolSection.identificationModule.nctId标题 …identificationModule.briefTitle状态 …statusModule.overallStatus阶段 …designModule.phases入组数 …designModule.enrollmentInfo.count入排标准 …eligibilityModule地点 …contactsLocationsModule.locations干预 …armsInterventionsModule.interventions结果 study.get('resultsSection')(无结果为 None)翻页:沿响应里的
nextPageToken走,把它回填进下次请求的pageToken,直到为空;批量循环加time.sleep(1.5)守速率。
示例
通用助手 + 按疾病检索招募试验:
import requests, time
CT_API = "https://clinicaltrials.gov/api/v2"
def ct_search(params):
r = requests.get(f"{CT_API}/studies", params=params, timeout=30)
r.raise_for_status()
return r.json()
results = ct_search({
"query.cond": "breast cancer",
"filter.overallStatus": "RECRUITING",
"pageSize": 10,
"sort": "LastUpdatePostDate:desc",
})
print(f"共 {results['totalCount']} 项试验")
for s in results["studies"][:3]:
ident = s["protocolSection"]["identificationModule"]
print(f' {ident["nctId"]}: {ident["briefTitle"]}')
按药物 + 阶段过滤(多状态逗号分隔,一次请求覆盖):
results = ct_search({
"query.intr": "Pembrolizumab",
"filter.overallStatus": "RECRUITING,ACTIVE_NOT_RECRUITING",
"filter.phase": "PHASE3",
"pageSize": 50,
})
print(f'Phase 3 Pembrolizumab 试验:{results["totalCount"]}')
按 NCT 编号取详情(入排标准 / 年龄 / 性别):
nct = "NCT04852770"
study = requests.get(f"{CT_API}/studies/{nct}", timeout=30).json()
proto = study["protocolSection"]
elig = proto.get("eligibilityModule", {})
print(proto["identificationModule"]["briefTitle"], proto["statusModule"]["overallStatus"])
print(f'年龄 {elig.get("minimumAge")}–{elig.get("maximumAge")},性别 {elig.get("sex")}')
print((elig.get("eligibilityCriteria", "N/A"))[:300])
大结果集翻页(最大页 + 守速率):
all_studies, token = [], None
for page in range(10): # 上限 10 页
params = {"query.cond": "cancer", "filter.overallStatus": "RECRUITING", "pageSize": 1000}
if token:
params["pageToken"] = token
data = ct_search(params)
all_studies.extend(data["studies"])
token = data.get("nextPageToken")
if not token:
break
time.sleep(1.5)
print(f"共取 {len(all_studies)} 项")
导出 CSV:
r = requests.get(f"{CT_API}/studies", params={
"query.cond": "heart disease", "filter.overallStatus": "RECRUITING",
"format": "csv", "pageSize": 1000}, timeout=60)
open("heart_disease_trials.csv", "w", encoding="utf-8").write(r.text)
安全取字段 + 提取摘要(应对缺模块):
def safe_get(study, *keys, default="N/A"):
cur = study
for k in keys:
cur = cur.get(k) if isinstance(cur, dict) else None
if cur is None:
return default
return cur
def extract_summary(study):
p = study.get("protocolSection", {})
d = p.get("designModule", {})
return {
"nct_id": safe_get(p, "identificationModule", "nctId"),
"title": p.get("identificationModule", {}).get("briefTitle"),
"status": safe_get(p, "statusModule", "overallStatus"),
"phases": d.get("phases", []),
"enrollment": d.get("enrollmentInfo", {}).get("count"),
}
带退避的健壮检索(批量必备,命中 429 时退避):
def ct_search_with_retry(params, max_retries=3):
for attempt in range(max_retries):
try:
r = requests.get(f"{CT_API}/studies", params=params, timeout=30)
r.raise_for_status()
return r.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
time.sleep(60) # 限流,等满 1 分钟
else:
raise
except requests.exceptions.RequestException:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
raise RuntimeError("超过最大重试次数")
注意事项
- 状态/阶段值大小写敏感:用
RECRUITING而非recruiting,否则 400 Bad Request。 - 多状态用逗号一次查完:
RECRUITING,NOT_YET_RECRUITING,别每个状态发一次请求。 - 访结果前先查
hasResults:多数试验无已发布结果,resultsSection缺失;直接访问会 KeyError。 - 嵌套字段一律
.get()兜底:contactsLocationsModule/armsInterventionsModule等并非每条试验都填充。 - 守速率(约 50/分钟):批量用
pageSize=1000减少请求数;命中 429 等 60s 后重试或指数退避。 - 翻页早停属正常:
nextPageToken缺失即已取完,用totalCount与已收集数核对。 - 默认排序建议
LastUpdatePostDate:desc,最新更新的试验优先;日期字段lastUpdatePostDateStruct.date为 ISO 8601 字符串,type标ACTUAL/ESTIMATED。 - CSV 与 JSON 结构不同:CSV 把嵌套结构扁平化;需要程序化访问字段时用 JSON。大批量 CSV 导出易超时,提高 timeout 或改用分页。
- 空
studies数组:多为过滤过严或拼写错误,放宽状态/阶段过滤或核对拼写。
互见
- related:
scientific-database-lookup—— 跨多个科研/生物医学库的统一 REST 查询入口 - related:
chembl-bioactivity-database—— 在研药物的化合物生物活性数据 - related:
opentargets-database—— 将药物-靶点证据关联到疾病 - combines_with:
research-experiment-designer—— 据试验注册信息设计/对标研究方案 - combines_with:
nih-grant-finder—— 试验组合分析与科研资助检索互补
参考:API 文档 https://clinicaltrials.gov/data-api/api | v1→v2 迁移 https://clinicaltrials.gov/data-api/about-api/api-migration | OpenAPI 规范 https://clinicaltrials.gov/data-api/about-api/api-spec | 主站 https://clinicaltrials.gov/
采编自 jaechang-hits/SciAgent-Skills(CC-BY-4.0)。