mcpField
D
dailymed-database

dailymed-database

github.com/jaechang-hits数据与数据库38472 次安装6 天前更新
代码仓库

关于 dailymed-database

dailymed-database 是查询 DailyMed 官方 FDA 药品标签的技能,面向药物研究与临床数据流程。它支持按药名、NDC、set ID 或 RxCUI 检索,并提取结构化标签内容。

主要功能

  1. 01按药名、NDC、set ID 检索
  2. 02读取完整药品标签
  3. 03提取 LOINC 标签章节
  4. 04查询包装与 NDC 信息
  5. 05通过 RxCUI 查找标签

适用场景

  1. 01比较品牌药与仿制药标签
  2. 02提取警告和不良反应
  3. 03按活性成分查找剂型
  4. 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、/drugnames201–100每页结果数;最多 100 条
page/spls、/drugnames1正整数分页页码
setid/spls/{setid}、/spls/{setid}/packaging—UUID 字符串唯一标签标识符;直接访问时必需
rxcui/rxcuis/{rxcui}/spls—RxNorm 概念 ID按 RxCUI 查找标签,用于临床系统集成
format任意端点jsonjson、xml响应格式;默认且首选 JSON

最佳实践

  1. 批量处理前先解析 set ID:使用 /spls 搜索获取 set ID,然后按 set ID 获取完整标签。不要根据药品名称构造 set ID——它们是由 FDA 分配的 UUID 标识符。

  2. 在批处理循环中添加 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
    
  3. 提取章节时使用 LOINC 代码,而不是解析文本:标签章节按 LOINC 代码编制索引,因此可以在所有 SPL 文档中一致地访问章节,无需对章节标题使用脆弱的正则表达式。

  4. 全面获取结果时使用分页:默认的 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)
    
  5. 临床系统集成时优先使用 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 Foundset 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 查询药物临床试验数据

参考资料

评论

支持 Markdown