关于 dailymed-database
dailymed-database 是查询 DailyMed 官方 FDA 药品标签的技能,面向药物研究与临床数据流程。它支持按药名、NDC、set ID 或 RxCUI 检索,并提取结构化标签内容。
主要功能
- 01按药名、NDC、set ID 检索
- 02读取完整药品标签
- 03提取 LOINC 标签章节
- 04查询包装与 NDC 信息
- 05通过 RxCUI 查找标签
适用场景
- 01比较品牌药与仿制药标签
- 02提取警告和不良反应
- 03按活性成分查找剂型
- 04构建药品标签数据流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill dailymed-database安装后在 Claude Code 中输入/dailymed-database确认已加载。
详细介绍
DailyMed 药品标签数据库
概述
DailyMed 是美国国家医学图书馆的官方存储库,收录 FDA 批准的药品标签信息,其中包含 140,000 多份处方药、非处方药、生物制品和疫苗的结构化产品标签(SPL)。REST API(v2)提供完整标签内容的结构化 JSON/XML 访问,包括适应症、剂量、警告、禁忌症、不良反应和包装数据,无需身份验证。
使用场景
- 按药品名称、NDC 代码或 set ID 获取 FDA 批准的官方处方信息
- 提取结构化标签章节(适应症、警告、剂量、不良反应),用于药理学研究
- 查询某种活性成分的所有上市剂型及其包装和 NDC 代码
- 使用 RxNorm 集成中的 RxCUI 标识符交叉引用药品标签
- 构建需要权威 FDA 标签内容(而非用户报告数据)的药品信息处理流水线
- 比较同一种药品的品牌名和仿制药剂型的标签内容
- 如需 FAERS 不良事件报告,请改用
fda-database;DailyMed 包含的是标签文本,而非上市后安全性信号 - 如需药物相互作用严重程度数据,请使用
ddinter-database;DailyMed 标签中的相互作用文本没有结构化
前提条件
- Python 包:
requests、pandas、matplotlib - 数据要求:药品名称、NDC 代码、set ID 或 RxCUI 标识符
- 环境:需要互联网连接;无需 API 密钥
- 速率限制:没有官方公布的限制;礼貌访问时每分钟约 100 次请求较为安全;批量循环中添加
time.sleep(0.3)
pip install requests pandas matplotlib
快速入门
import requests
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
# Search drug labels by name
r = requests.get(f"{BASE}/spls.json", params={"drug_name": "metformin", "pagesize": 5})
r.raise_for_status()
data = r.json()
print(f"Total labels found: {data['metadata']['total_elements']}")
for spl in data["data"][:3]:
print(f" {spl['title']!r:60s} setid={spl['setid']}")
核心 API
查询 1:按名称搜索药品标签
使用药品名称搜索结构化产品标签(SPL)。返回匹配标签的分页列表及其 set ID。
import requests
import pandas as pd
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def search_spls(drug_name, pagesize=20, page=1):
"""Search DailyMed SPLs by drug name. Returns list of label summaries."""
r = requests.get(f"{BASE}/spls.json",
params={"drug_name": drug_name, "pagesize": pagesize, "page": page},
timeout=15)
r.raise_for_status()
return r.json()
result = search_spls("atorvastatin", pagesize=10)
meta = result["metadata"]
print(f"Search: 'atorvastatin' → {meta['total_elements']} labels across {meta['total_pages']} pages")
df = pd.DataFrame(result["data"])
print(df[["setid", "title", "published_date"]].to_string(index=False))
# setid title published_date
# 8f6c7c7c-... ATORVASTATIN CALCIUM tablet 2024-03-15
# a4b7d3e1-... ATORVASTATIN CALCIUM tablet, film coated 2023-11-20
查询 2:按 Set ID 获取完整标签
使用 set ID 获取特定药品的完整结构化产品标签。返回所有标签章节,包括适应症、警告、剂量和不良反应。
import requests
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def get_spl(setid):
"""Retrieve full SPL document by set ID. Returns label metadata and XML/JSON."""
r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
r.raise_for_status()
return r.json()
# Use a known set ID from search results
setid = "8f6c7c7c-1f7f-4f1a-af86-8b2eef2a8b2c" # example atorvastatin label
label = get_spl(setid)
data = label["data"]
print(f"Title: {data.get('title')}")
print(f"Set ID: {data.get('setid')}")
print(f"Published: {data.get('published_date')}")
print(f"Version: {data.get('version')}")
# Access structured sections
if "sections" in data:
sections = data["sections"]
print(f"\nLabel sections ({len(sections)} total):")
for sec in sections[:5]:
print(f" [{sec.get('loinc_code', 'N/A')}] {sec.get('title', 'Untitled')}")
查询 3:按 NDC 代码搜索
按国家药品代码(NDC)查询药品标签——适用于持有产品条形码或配药记录的情况。
import requests
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def search_by_ndc(ndc_code):
"""Find SPL by NDC code (formatted as XXXXX-XXXX-XX or without dashes)."""
r = requests.get(f"{BASE}/spls.json",
params={"ndc": ndc_code},
timeout=15)
r.raise_for_status()
return r.json()
# NDC for Lipitor 10mg (atorvastatin)
result = search_by_ndc("0071-0155-23")
if result["data"]:
spl = result["data"][0]
print(f"Drug: {spl['title']}")
print(f"Set ID: {spl['setid']}")
print(f"Published: {spl['published_date']}")
else:
print("No label found for this NDC")
查询 4:获取包装信息
按 set ID 获取特定药品标签的详细包装数据(NDC 代码、包装类型、数量)。
import requests
import pandas as pd
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def get_packaging(setid):
"""Retrieve packaging information for a label (NDC codes, dosage forms, quantities)."""
r = requests.get(f"{BASE}/spls/{setid}/packaging.json", timeout=15)
r.raise_for_status()
return r.json()
setid = "8f6c7c7c-1f7f-4f1a-af86-8b2eef2a8b2c" # example setid
pkg = get_packaging(setid)
if pkg["data"]:
packages = pkg["data"]
print(f"Packaging variants: {len(packages)}")
df = pd.DataFrame(packages)
# Common fields: ndc, dosage_form, route, marketing_status
for col in ["ndc", "dosage_form", "route", "marketing_status"]:
if col in df.columns:
print(f"\n{col.upper()}:")
print(df[col].value_counts().head(5))
查询 5:按 RxCUI 查询
使用 RxNorm 概念唯一标识符(RxCUI)获取药品标签,以便与基于 RxNorm 的临床系统集成。
import requests
import time
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def get_spls_by_rxcui(rxcui):
"""Get all SPLs associated with an RxCUI. Returns list of set IDs and titles."""
r = requests.get(f"{BASE}/rxcuis/{rxcui}/spls.json", timeout=15)
r.raise_for_status()
return r.json()
# RxCUI for atorvastatin: 83367
rxcui = "83367"
result = get_spls_by_rxcui(rxcui)
print(f"SPLs for RxCUI {rxcui} (atorvastatin):")
for spl in result["data"][:5]:
print(f" {spl['setid']} | {spl['title'][:70]}")
print(f" Total: {result['metadata']['total_elements']} labels")
查询 6:搜索药品名称索引
列出 DailyMed 中所有标准化药品名称——适用于药品查询流水线中的名称规范化和自动补全。
import requests
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def search_drug_names(query, pagesize=20):
"""Search the DailyMed drug name index for name normalization."""
r = requests.get(f"{BASE}/drugnames.json",
params={"drug_name": query, "pagesize": pagesize},
timeout=15)
r.raise_for_status()
return r.json()
result = search_drug_names("metformin")
print(f"Drug name matches for 'metformin': {result['metadata']['total_elements']}")
for entry in result["data"][:8]:
print(f" {entry['drug_name']}")
# METFORMIN HYDROCHLORIDE
# METFORMIN HYDROCHLORIDE AND SITAGLIPTIN PHOSPHATE
# METFORMIN HYDROCHLORIDE AND SAXAGLIPTIN
查询 7:批量提取标签章节
从多个药品标签中提取特定章节(例如适应症、警告),用于比较分析。
import requests
import time
import pandas as pd
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
# LOINC codes for common SPL sections
SECTION_LOINC = {
"34067-9": "Indications and Usage",
"34068-7": "Dosage and Administration",
"34071-1": "Warnings",
"34084-4": "Adverse Reactions",
"34070-3": "Contraindications",
"43685-7": "Warnings and Precautions",
}
def get_label_sections(setid):
"""Extract structured sections from an SPL by set ID."""
r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
r.raise_for_status()
data = r.json()["data"]
sections = {}
for sec in data.get("sections", []):
loinc = sec.get("loinc_code")
if loinc in SECTION_LOINC:
sections[SECTION_LOINC[loinc]] = sec.get("text", "")
return sections
# Get indications for two statin labels
statins = [
("Atorvastatin", "8f6c7c7c-1f7f-4f1a-af86-8b2eef2a8b2c"),
("Rosuvastatin", "a3b2c4d5-1234-5678-abcd-ef0123456789"), # example
]
records = []
for drug_name, setid in statins:
try:
sections = get_label_sections(setid)
records.append({"drug": drug_name, "indications_length": len(sections.get("Indications and Usage", ""))})
except Exception as e:
print(f"Warning: {drug_name} failed — {e}")
time.sleep(0.3)
df = pd.DataFrame(records)
print(df.to_string(index=False))
核心概念
结构化产品标签(SPL)和 Set ID
SPL 是 FDA 批准的官方药品标签,采用 XML 格式,并按照 Health Level 7(HL7)临床文档架构(CDA)进行结构化。每份独特的药品产品标签都有全局唯一的 set ID(UUID 格式)。同一标签的多个版本共享相同的 set ID,但版本号不同。获取当前处方信息时,务必使用最近发布的版本。
LOINC 章节代码
DailyMed SPL 使用标准化 LOINC 代码标识标签章节,以便在所有标签中一致地进行程序化提取:
| LOINC 代码 | 章节名称 |
|---|---|
34067-9 | 适应症和用法 |
34068-7 | 剂量和给药 |
34070-3 | 禁忌症 |
34071-1 | 警告 |
43685-7 | 警告和注意事项 |
34084-4 | 不良反应 |
34073-7 | 药物相互作用 |
34076-0 | 患者咨询信息 |
42229-5 | 作用机制 |
34069-5 | 供应形式/储存 |
NDC 代码格式
国家药品代码(NDC)采用由三个数字段组成的格式来标识药品:labeler-product-package(例如,0071-0155-23)。某些系统中也可能会显示不带连字符的 NDC。DailyMed 在 API 查询中接受这两种格式。
常见工作流
工作流 1:按活性成分比较药品标签
目标:检索并比较某种活性成分的所有剂型标签内容,适用于仿制药与品牌药的比较。
import requests
import time
import pandas as pd
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def search_spls(drug_name, pagesize=50):
r = requests.get(f"{BASE}/spls.json",
params={"drug_name": drug_name, "pagesize": pagesize},
timeout=15)
r.raise_for_status()
return r.json()
def get_packaging(setid):
r = requests.get(f"{BASE}/spls/{setid}/packaging.json", timeout=15)
r.raise_for_status()
return r.json()
# Search for all metformin labels
result = search_spls("metformin", pagesize=20)
labels = result["data"]
print(f"Found {len(labels)} metformin labels")
rows = []
for label in labels[:10]: # limit for demo
setid = label["setid"]
try:
pkg = get_packaging(setid)
for item in pkg["data"][:2]:
rows.append({
"title": label["title"][:60],
"setid": setid,
"ndc": item.get("ndc"),
"dosage_form": item.get("dosage_form"),
"route": item.get("route"),
"marketing_status": item.get("marketing_status"),
"published_date": label.get("published_date"),
})
except Exception as e:
print(f"Skipping {setid}: {e}")
time.sleep(0.3)
df = pd.DataFrame(rows)
print(f"\nFormulations with packaging data: {len(df)}")
print(df[["title", "dosage_form", "route", "marketing_status"]].drop_duplicates().to_string(index=False))
df.to_csv("metformin_formulations.csv", index=False)
print("\nSaved: metformin_formulations.csv")
工作流 2:提取警告和不良反应以进行安全性分析
目标:系统地从一组药品标签中提取结构化的警告和不良反应文本,用于药物警戒研究。
import requests
import time
import pandas as pd
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
TARGET_SECTIONS = {
"34071-1": "Warnings",
"43685-7": "Warnings_and_Precautions",
"34084-4": "Adverse_Reactions",
"34070-3": "Contraindications",
}
def search_and_get_sections(drug_name, max_labels=5):
"""Search for labels and extract safety-relevant sections."""
search_r = requests.get(f"{BASE}/spls.json",
params={"drug_name": drug_name, "pagesize": max_labels},
timeout=15)
search_r.raise_for_status()
labels = search_r.json()["data"][:max_labels]
records = []
for label in labels:
setid = label["setid"]
try:
label_r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
label_r.raise_for_status()
spl_data = label_r.json()["data"]
row = {"drug_name": drug_name, "title": label["title"][:80], "setid": setid}
for loinc, col in TARGET_SECTIONS.items():
text = ""
for sec in spl_data.get("sections", []):
if sec.get("loinc_code") == loinc:
text = sec.get("text", "")[:500] # first 500 chars
break
row[col] = text
records.append(row)
except Exception as e:
print(f" Skipping {setid}: {e}")
time.sleep(0.3)
return pd.DataFrame(records)
# Compare safety sections across ACE inhibitor labels
drugs = ["lisinopril", "enalapril"]
all_records = []
for drug in drugs:
print(f"Processing: {drug}")
df = search_and_get_sections(drug, max_labels=3)
all_records.append(df)
combined = pd.concat(all_records, ignore_index=True)
print(f"\nExtracted safety sections: {len(combined)} labels")
print(combined[["drug_name", "title"]].to_string(index=False))
combined.to_csv("ace_inhibitor_safety_sections.csv", index=False)
print("Saved: ace_inhibitor_safety_sections.csv")
工作流 3:按药物类别可视化标签分布
目标:查询某一类别中的多种药物,统计其标签剂型数量,并将分布可视化。
import requests
import time
import pandas as pd
import matplotlib.pyplot as plt
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def count_labels(drug_name):
"""Return total SPL count for a drug name."""
r = requests.get(f"{BASE}/spls.json",
params={"drug_name": drug_name, "pagesize": 1},
timeout=15)
r.raise_for_status()
return r.json()["metadata"]["total_elements"]
# Count labels for common statins
statins = {
"atorvastatin": "Atorvastatin",
"rosuvastatin": "Rosuvastatin",
"simvastatin": "Simvastatin",
"pravastatin": "Pravastatin",
"lovastatin": "Lovastatin",
"fluvastatin": "Fluvastatin",
"pitavastatin": "Pitavastatin",
}
counts = {}
for query, label in statins.items():
try:
counts[label] = count_labels(query)
print(f" {label}: {counts[label]} labels")
except Exception as e:
print(f" {label}: error — {e}")
time.sleep(0.3)
# Visualization
df = pd.DataFrame(list(counts.items()), columns=["Drug", "Label_Count"])
df = df.sort_values("Label_Count", ascending=True)
fig, ax = plt.subplots(figsize=(9, 5))
bars = ax.barh(df["Drug"], df["Label_Count"], color="#2196F3", edgecolor="white")
ax.bar_label(bars, fmt="%d", padding=4, fontsize=9)
ax.set_xlabel("Number of DailyMed SPL Entries")
ax.set_title("DailyMed: FDA Drug Label Count by Statin\n(includes brand + generic formulations)")
ax.set_xlim(0, df["Label_Count"].max() * 1.15)
plt.tight_layout()
plt.savefig("statin_label_counts.png", dpi=150, bbox_inches="tight")
print(f"Saved: statin_label_counts.png (total labels: {df['Label_Count'].sum()})")
关键参数
| 参数 | 端点 | 默认值 | 范围 / 选项 | 作用 |
|---|---|---|---|---|
drug_name | /spls、/drugnames | — | 任意药品名称字符串 | 按药品名称筛选标签(支持部分匹配) |
ndc | /spls | — | NDC 代码字符串 | 按国家药品代码筛选标签 |
pagesize | /spls、/drugnames | 20 | 1–100 | 每页结果数;最多 100 条 |
page | /spls、/drugnames | 1 | 正整数 | 分页页码 |
setid | /spls/{setid}、/spls/{setid}/packaging | — | UUID 字符串 | 唯一标签标识符;直接访问时必需 |
rxcui | /rxcuis/{rxcui}/spls | — | RxNorm 概念 ID | 按 RxCUI 查找标签,用于临床系统集成 |
format | 任意端点 | json | json、xml | 响应格式;默认且首选 JSON |
最佳实践
-
批量处理前先解析 set ID:使用
/spls搜索获取 set ID,然后按 set ID 获取完整标签。不要根据药品名称构造 set ID——它们是由 FDA 分配的 UUID 标识符。 -
在批处理循环中添加
time.sleep(0.3):DailyMed 未公布速率限制,但它属于 NIH 公共基础设施。适当延迟有助于避免请求被限流。import time for drug in drug_list: result = search_spls(drug) time.sleep(0.3) # 200 requests/minute safe upper bound -
提取章节时使用 LOINC 代码,而不是解析文本:标签章节按 LOINC 代码编制索引,因此可以在所有 SPL 文档中一致地访问章节,无需对章节标题使用脆弱的正则表达式。
-
全面获取结果时使用分页:默认的
pagesize=20可能会遗漏某些剂型。使用metadata["total_elements"]判断是否需要分页:meta = result["metadata"] total_pages = meta["total_pages"] if total_pages > 1: for p in range(2, total_pages + 1): result = search_spls(drug_name, page=p) -
临床系统集成时优先使用 RxCUI:与临床系统(EHR、配药系统)集成时,使用基于 RxCUI 的查询(
/rxcuis/{rxcui}/spls),以便通过标准化且明确无歧义的方式识别药品。
常用操作示例
示例:检查药品是否带有黑框警告
适用场景:快速确定某种药品是否带有 FDA 最高级别的警告。
import requests
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
BLACK_BOX_LOINC = "34066-1"
def has_black_box_warning(setid):
"""Return True and warning text if drug label has a black box (boxed) warning."""
r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
r.raise_for_status()
sections = r.json()["data"].get("sections", [])
for sec in sections:
if sec.get("loinc_code") == BLACK_BOX_LOINC:
return True, sec.get("text", "")[:300]
return False, ""
# Example: check warfarin label
setid = "8b7c3d4e-5678-90ab-cdef-1234567890ab" # example warfarin setid
has_warning, text = has_black_box_warning(setid)
print(f"Black box warning: {has_warning}")
if has_warning:
print(f"Warning text (first 300 chars): {text}")
示例:多页搜索并获取所有结果
适用场景:当结果总数超过一页时,获取某种药品的所有匹配标签。
import requests
import time
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def search_all_spls(drug_name, pagesize=100, delay=0.3):
"""Retrieve all SPLs for a drug name across multiple pages."""
all_labels = []
page = 1
while True:
r = requests.get(f"{BASE}/spls.json",
params={"drug_name": drug_name, "pagesize": pagesize, "page": page},
timeout=15)
r.raise_for_status()
data = r.json()
all_labels.extend(data["data"])
if page >= data["metadata"]["total_pages"]:
break
page += 1
time.sleep(delay)
return all_labels
labels = search_all_spls("ibuprofen")
print(f"Total ibuprofen labels: {len(labels)}")
示例:将标签摘要导出为 CSV
适用场景:构建药品标签的扁平参考表,以便离线分析。
import requests
import pandas as pd
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def export_drug_labels(drug_name, pagesize=50):
"""Export label summaries for a drug name to DataFrame."""
r = requests.get(f"{BASE}/spls.json",
params={"drug_name": drug_name, "pagesize": pagesize},
timeout=15)
r.raise_for_status()
data = r.json()
df = pd.DataFrame(data["data"])
df["drug_query"] = drug_name
return df, data["metadata"]["total_elements"]
df, total = export_drug_labels("amoxicillin")
print(f"Retrieved {len(df)} of {total} amoxicillin labels")
df[["setid", "title", "published_date"]].to_csv("amoxicillin_labels.csv", index=False)
print(f"Saved: amoxicillin_labels.csv (columns: {list(df.columns[:5])})")
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
/spls/{setid} 返回 404 Not Found | set ID 无效或已停用 | 按药品名称重新搜索,以获取当前的 set ID |
搜索返回的 data 列表为空 | 药品名称与任何标签都不匹配 | 尝试使用较短的名称(例如,"metformin" 而非 "metformin HCl tablets");检查拼写 |
| SPL JSON 中缺少章节 | 较旧的标签可能不包含所有采用 LOINC 编码的章节 | 遍历前检查 len(sections);对于旧版标签,改用 XML 格式 |
ConnectionError / 超时 | DailyMed 服务过载 | 使用指数退避重试;将 timeout=30 调大 |
| 分页结果中出现重复项 | 页面边界竞态条件 | 收集完所有页面后,按 setid 去重 |
| 包装信息端点返回空结果 | 标签存在,但没有包装记录 | 有些生物制品和疫苗没有包装数据;直接使用标签数据 |
| RxCUI 查询无结果 | RxCUI 未映射到 DailyMed | 查询前通过 RxNorm API 验证 RxCUI;部分实验性药物未映射 |
相关技能
fda-database— 使用 openFDA 查询不良事件报告(FAERS)、药品召回和执法行动ddinter-database— 查询 DDInter 提供的药物相互作用严重程度和作用机制drugbank-database-access— 获取全面的药物信息,包括靶点、通路和化学性质clinicaltrials-database-search— 使用 ClinicalTrials.gov 查询药物临床试验数据
参考资料
- DailyMed — 官方药品标签库(NLM/NIH)
- DailyMed Web Services API v2 — 完整的 REST API 文档
- SPL Standard (HL7) — 结构化产品标签标准文档
- LOINC Document Ontology — 用于识别 SPL 章节的 LOINC 编码
- NLM DailyMed Overview — 数据覆盖范围和更新频率