mcpField
Z
zinc-database

zinc-database

github.com/jaechang-hits数据与数据库38468 次安装1 周前更新
代码仓库

关于 zinc-database

ZINC22 是面向虚拟筛选的可购化合物数据库,通过 CartBlanche 提供查询接口,适用于药物发现和分子对接工作。它可按 ZINC ID 查询化合物记录,也支持由 SMILES 精确查找标识符及供应商信息。

主要功能

  1. 01按 ZINC ID 获取化合物记录
  2. 02将 SMILES 精确映射到 ZINC ID
  3. 03查看供应商目录与可购状态
  4. 04获取分子属性和结构标识符
  5. 05使用 RDKit 在本地筛选属性

适用场景

  1. 01核查候选化合物是否可购买
  2. 02将 SMILES 候选集整理为 ZINC 标识符
  3. 03为分子对接准备化合物集合
  4. 04用 RDKit 按属性筛选候选分子

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill zinc-database

安装后在 Claude Code 中输入/zinc-database确认已加载。

详细介绍

ZINC 化学品库数据库(ZINC22 / CartBlanche)

概览

ZINC(ZINC Is Not Commercial)是一个免费的商业可购化合物数据库,专为虚拟筛选整理。当前版本 ZINC22 收录了数十亿种按需合成和现货分子,并通过 CartBlanche 网页应用提供服务,网址为 cartblanche22.docking.org。CartBlanche 提供了一个小型 JSON API,可用于按 ZINC ID 查询物质、将 SMILES 解析为对应的 ZINC 标识符,以及查看供应商和目录中的可购情况。

重要提示——旧版 ZINC15 REST API 已无法用于自动化访问。 zinc15.docking.org(以及 zinc.docking.org)现在启用了全站 CAPTCHA:每次请求 /substances.json、/tranches/...、/substances/{id}.json 等路径时,都会被重定向到 /captcha/ 页面或返回 403 Forbidden。任何使用 mwt__gte / availability / similarity 查询参数访问 zinc15.docking.org 的脚本都会失败。请改用下文介绍的 ZINC22 CartBlanche 端点。

关于 ZINC22/CartBlanche,以下三点会影响此技能的使用方式:

  1. 不支持服务器端属性范围查询。 ZINC22/CartBlanche 不支持 mwt__gte / logp__lte / hbd__lte 搜索。按分子属性筛选的方法有两种:(a) 选择 tranches(MW x logP 二维网格)进行批量下载,或 (b) 获取化合物后用 RDKit 在本地筛选。本技能介绍使用本地 RDKit 的方法。
  2. SMILES 查询是异步的。 POST 提交 SMILES 后会收到一个 task ID,之后需轮询获取结果。精确匹配查询可在数秒内完成。
  3. 可靠的程序化搜索 = 精确匹配。 API 范围更广的类似物搜索(dist > 0)速度很慢,而且在公共服务上经常超时;Smallworld 相似性搜索仅支持通过网站操作。请将精确 SMILES → ZINC ID 查询视为可靠的基础操作;有限的替代方法请参阅“类似物 / 相似性搜索”说明。

适用场景

  • 查询已知 ZINC ID,获取其 SMILES、计算属性以及可购情况/供应商信息
  • 将 SMILES 格式的分子解析为对应的 ZINC22 标识符,以确认是否有货
  • 下单前确认命中化合物是否可购,以及哪些目录中有售
  • 整理 SMILES/ZINC ID 集合,用于对接筛选活动
  • 构建按属性筛选的化合物库时,将此处获取的 SMILES/tranche 与本地 RDKit 筛选结合使用(rdkit-cheminformatics)
  • 查询已知药物的生物活性数据,请使用 chembl-database-bioactivity;查询已批准药物的结构,请使用 drugbank-database-access

前提条件

  • Python 包:requests、pandas(本地属性筛选还需 rdkit)
  • 数据要求:ZINC ID 或 SMILES 字符串
  • 环境:需要互联网连接;无需 API 密钥;SMILES 查询为异步操作(需轮询获取结果)
  • 速率限制:请合理使用——串行执行搜索、缓存结果,并且轮询 task 端点的间隔不要短于几秒
pip install requests pandas
# optional, for local property filtering:
pip install rdkit

快速开始

import requests

BASE = "https://cartblanche22.docking.org"
HEADERS = {"User-Agent": "sciagent-zinc-skill/1.0"}

# Look up a substance by ZINC ID (synchronous, returns JSON immediately)
r = requests.get(f"{BASE}/substance/ZINC000000029632.json", headers=HEADERS, timeout=30)
r.raise_for_status()
c = r.json()

td = c["tranche_details"]
print(f"ZINC ID : {c['zinc_id']}  (db: {c['db']})")
print(f"SMILES  : {c['smiles']}")
print(f"MW      : {td['mwt']:.2f}   logP: {td['logp']:.2f}   heavy atoms: {td['heavy_atoms']}")
print(f"InChIKey: {td['inchikey']}")
print(f"Catalogs: {len(c.get('catalogs', []))} supplier entries")

预期输出(节选):

ZINC ID : ZINC000000029632  (db: zinc20)
SMILES  : O=C([C@H]1CCCN1C(=O)Cc1c[nH]c2ccccc12)N1CCc2ccccc2C1
MW      : 387.48   logP: 3.29   heavy atoms: 29
InChIKey: QCEMBLKSDIWPBK-JOCHJYFZSA-N
Catalogs: 4 supplier entries

核心 API

CartBlanche 基础 URL 和下文会重复使用的两个辅助函数:

import requests, time

BASE = "https://cartblanche22.docking.org"
HEADERS = {"User-Agent": "sciagent-zinc-skill/1.0"}

def get_substance(zinc_id):
    """Fetch full record for one ZINC ID (synchronous)."""
    r = requests.get(f"{BASE}/substance/{zinc_id}.json", headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()

def smiles_lookup(smiles, dist=0, adist=0, database="zinc22", timeout=120, poll=5):
    """Resolve a SMILES to ZINC22 substance dicts via the async task API.

    dist=0, adist=0 -> exact match (fast, reliable; use this).
    dist/adist > 0  -> near-neighbor analog search (slow; may time out — see note).
    Returns a list of substance dicts (possibly empty).
    """
    files = {"smiles": (None, smiles), "dist": (None, str(dist)),
             "adist": (None, str(adist)), "database": (None, database)}
    sub = requests.post(f"{BASE}/smiles.json", files=files, headers=HEADERS, timeout=60)
    sub.raise_for_status()
    task = sub.json()["task"]                       # async: returns {"task": "<uuid>"}

    deadline = time.time() + timeout
    while time.time() < deadline:
        res = requests.get(f"{BASE}/search/result/{task}", headers=HEADERS, timeout=30).json()
        if res.get("status") == "SUCCESS":
            data = res.get("result")
            # result is {"zinc22": [...], "zinc20": [...]} when hits exist,
            # or an empty list when there are none.
            if isinstance(data, dict):
                return data.get("zinc22", []) + data.get("zinc20", [])
            return data or []
        time.sleep(poll)                            # status == "PROGRESS" until done
    raise TimeoutError(f"ZINC22 search task {task} did not finish in {timeout}s")
查询 1:按 ZINC ID 获取化合物

获取已知 ZINC 标识符对应的完整记录(SMILES、计算属性、分子式、环和杂原子数量、供应商目录)。

c = get_substance("ZINC000000029632")
td = c["tranche_details"]

print(f"ZINC ID    : {c['zinc_id']}")
print(f"SMILES     : {c['smiles']}")
print(f"Formula    : {c['mol_formula']}")
print(f"MW         : {td['mwt']:.2f}")
print(f"logP       : {td['logp']:.2f}")
print(f"Heavy atoms: {td['heavy_atoms']}")
print(f"Rings      : {c['rings']}   Hetero atoms: {c['hetero_atoms']}")
print(f"InChIKey   : {td['inchikey']}")
print(f"Suppliers  : {len(c.get('catalogs', []))}")

现代格式的 ZINCbq.../ZINCbj... ID 和旧版 12 位 ZINC000... ID 均可通过此端点解析。

查询 2:从 SMILES 解析 ZINC ID(精确匹配)

查找 SMILES 字符串所表示分子对应的 ZINC22 标识符。此方法是可靠的搜索基础操作。

hits = smiles_lookup("CC(=O)Nc1ccc(O)cc1")   # acetaminophen, exact (dist=0)
print(f"Exact matches: {len(hits)}")
for h in hits[:5]:
    td = h["tranche_details"]
    print(f"  {h['zinc_id']:22s}  MW {td['mwt']:.1f}  logP {td['logp']:.2f}  {h['smiles']}")

同一个分子可能对应 ZINC22 子数据库中的多个 ZINC ID(例如 ZINCbq... 和 ZINCbj...)——每个 ID 都代表不同构建版本中的同一结构。

查询 3:可购买性和供应商目录

可购买性已嵌入物质记录的 catalogs 中,无需单独请求。

c = get_substance("ZINC000000029632")
catalogs = c.get("catalogs", [])
print(f"Supplier entries: {len(catalogs)}")
for cat in catalogs:
    print(f"  {cat.get('catalog_name', 'n/a'):24s} | "
          f"purchasable: {cat.get('purchase')} | "
          f"code: {cat.get('supplier_code', 'n/a')} | "
          f"price: {cat.get('price', 'n/a')} {cat.get('unit', '')}")

purchase: 1 表示该化合物可从该目录订购;条目可能包含价格、数量和交货时间(shipping)。

查询 4:按属性筛选分子库(使用 RDKit 本地筛选)

ZINC22 不支持服务器端 MW/logP 查询。通用做法是:收集候选 SMILES(已解析的 ZINC ID、下载的分块文件或自行枚举的结果),然后使用 RDKit 在本地筛选。

import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors, Lipinski

# Candidate SMILES (here: a small hand list; in practice load a tranche .smi or your own set)
smiles_list = [
    "CC(=O)Nc1ccc(O)cc1",            # acetaminophen  (MW 151 - too small, filtered out)
    "CC(C)NCC(O)COc1cccc2ccccc12",   # propranolol    (MW 259, logP 2.6 - lead-like)
    "CN(C)CCOC(c1ccccc1)c1ccccc1",   # diphenhydramine
    "c1ccc(NC(=O)c2ccccc2)cc1",      # benzanilide    (MW 197 - too small, filtered out)
]

def lead_like(smi):
    m = Chem.MolFromSmiles(smi)
    if m is None:
        return None
    mw, logp = Descriptors.MolWt(m), Descriptors.MolLogP(m)
    hbd, hba = Lipinski.NumHDonors(m), Lipinski.NumHAcceptors(m)
    ok = 250 <= mw <= 350 and 1 <= logp <= 3 and hbd <= 3 and hba <= 7
    return {"smiles": Chem.MolToSmiles(m), "mw": round(mw, 1), "logp": round(logp, 2),
            "hbd": hbd, "hba": hba} if ok else None

rows = [r for r in (lead_like(s) for s in smiles_list) if r]
df = pd.DataFrame(rows).drop_duplicates(subset=["smiles"])
print(f"Lead-like compounds after RDKit filter: {len(df)}")
print(df.head())
类似物/相似性搜索(功能有限——使用前请阅读)

提交端点接受 dist(拓扑图编辑距离,0–3)和 adist(“anon”距离,即骨架跳数),用于近邻类似物搜索:

# Near-neighbor analogs. Keep dist small (1) and the timeout generous; expect FEW results.
try:
    analogs = smiles_lookup("c1ccc(NC(=O)c2ccccc2)cc1", dist=1, adist=0, timeout=240)
    print(f"Analogs: {len(analogs)}")
    for h in analogs[:10]:
        print("  ", h["zinc_id"], h["smiles"])
except TimeoutError as e:
    print("Analog search timed out:", e)

根据对在线公共服务的验证,注意事项如下:

  • dist=0(精确搜索)速度快且可靠。dist >= 1 会在共享集群上跨数十亿个分子运行,经常超时(例如,dist=3 在约 270 秒内未返回结果);即使搜索成功,通常也只会返回查询对象和少量近乎相同的结构。
  • 对于广泛的整分子相似性搜索(基于图编辑距离计算 Tanimoto),CartBlanche 在网站上提供单独的 Smallworld 搜索(cartblanche22.docking.org → Similarity → Smallworld,/similarity/sw)。这是交互式流程,此处未提供简单的 GET/POST JSON 端点——大型相似性搜索请使用网页界面。
  • 不要构建假设类似物搜索会返回丰富结果集的自动化流水线。应基于精确查询和本地 RDKit/化学信息学扩展来设计工作流。

关键概念

ZINC22 ID

ZINC22 引入了 ZINCbq0000001gLA 这类短字母数字 ID(前缀字母表示子数据库/分块)。ZINC000000029632 这类旧版 12 位 ID(来自 ZINC15/ZINC20)仍可通过 /substance/{id}.json 解析。请始终按原样传入 ID。

异步搜索模型

SMILES 搜索会作为任务排队:

  1. POST /smiles.json(multipart 表单:smiles、dist、adist、database)→ {"task": "<uuid>"}
  2. GET /search/result/{task} → 运行期间返回 {"status": "PROGRESS"},完成后返回 {"status": "SUCCESS", "result": {...}}
  3. 成功时,如果有命中结果,result 是一个以子数据库(zinc22,有时也包括 zinc20)为键的字典;如果没有结果,则为空列表。上面的辅助函数会规范化这两种形式。
分块

ZINC 根据重原子数/MW(H 代码,例如 H11)和 logP(字母分箱),将分子组织成二维的“分块”网格。CartBlanche 的 Tranches 浏览器(cartblanche22.docking.org 上的“3D”/“2D”分块页面)支持选择属性区域,并批量下载用于对接的 SMILES/SDF。分块批量下载是交互式的(且数据量较大);如需以编程方式筛选属性,请优先采用查询 4 中的 RDKit 方法,或从 files.docking.org 获取分块文件并在本地筛选。

结果字段

每条搜索/物质记录包含:zinc_id、smiles、mol_formula、rings、hetero_atoms、db、catalogs[] 和 tranche_details(mwt、logp、heavy_atoms、inchi、inchikey)。SMILES 搜索结果行还包含 sub_id、matched_smiles 和 tranche 代码。

常见工作流

工作流 1:检查命中列表的可购买性

目标:给定对接命中的 SMILES,解析其 ZINC22 ID,并报告哪些可以购买。

import pandas as pd

hit_smiles = [
    "CC(=O)Nc1ccc(O)cc1",            # acetaminophen
    "c1ccc(NC(=O)c2ccccc2)cc1",      # benzanilide
]

rows = []
for smi in hit_smiles:
    matches = smiles_lookup(smi)                   # exact
    if not matches:
        rows.append({"query_smiles": smi, "zinc_id": None, "purchasable": False})
        continue
    for m in matches:
        cats = m.get("catalogs", [])
        rows.append({
            "query_smiles": smi,
            "zinc_id": m["zinc_id"],
            "mw": m["tranche_details"]["mwt"],
            "n_catalogs": len(cats),
            "purchasable": any(c.get("purchase") for c in cats) or bool(cats),
        })

df = pd.DataFrame(rows)
print(df)
df.to_csv("hit_purchasability.csv", index=False)
print("Saved: hit_purchasability.csv")
工作流 2:解析并筛选用于对接的候选集合

目标:获取一组候选 SMILES(自行枚举的结果或分块文件),保留 ZINC22 中存在/可购买的分子,筛选出符合先导化合物特征的范围,并导出用于对接的 SMILES。

import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors

candidates = [
    "CC(=O)Nc1ccc(O)cc1",
    "O=C([C@H]1CCCN1C(=O)Cc1c[nH]c2ccccc12)N1CCc2ccccc2C1",
    "c1ccc(NC(=O)c2ccccc2)cc1",
]

rows = []
for smi in candidates:
    m = Chem.MolFromSmiles(smi)
    if m is None:
        continue
    mw, logp = Descriptors.MolWt(m), Descriptors.MolLogP(m)
    if not (200 <= mw <= 500 and -1 <= logp <= 5):     # lead-like local filter
        continue
    matches = smiles_lookup(smi)                        # confirm it exists in ZINC22
    if not matches:
        continue
    m0 = matches[0]
    rows.append({"zinc_id": m0["zinc_id"], "smiles": Chem.MolToSmiles(m),
                 "mw": round(mw, 1), "logp": round(logp, 2),
                 "purchasable": bool(m0.get("catalogs"))})

df = pd.DataFrame(rows).drop_duplicates(subset=["smiles"])
print(f"ZINC22-confirmed lead-like compounds: {len(df)}")
df[["smiles", "zinc_id"]].to_csv("docking_library.smi", sep=" ", index=False, header=False)
print("Saved: docking_library.smi")
print(df)

关键参数

参数端点默认值范围/选项作用
(路径)GET /substance/{zinc_id}.json—ZINC ID同步查询物质
smilesPOST /smiles.json—有效的 SMILES查询分子
distPOST /smiles.json00–3拓扑距离;0 = 精确匹配(可靠),>=1 = 类似物搜索(速度慢,可能超时)
adistPOST /smiles.json00–3匿名图距离(骨架跳跃);0 = 关闭
databasePOST /smiles.jsonzinc22"zinc22"、"zinc20"要搜索的 ZINC 版本
taskGET /search/result/{task}—提交时返回的 UUID用于轮询异步结果的句柄

最佳实践

  1. 使用 ZINC22(CartBlanche),不要使用 ZINC15。 zinc15.docking.org REST API 有 CAPTCHA 验证,无法通过脚本使用。此处所有端点均指向 cartblanche22.docking.org。

  2. 优先精确查询;不要依赖类似物搜索。 dist=0 是可靠的基础操作。对于类似物/相似度搜索,可在本地扩展搜索范围(RDKit、rdkit-cheminformatics),或使用 Smallworld 网页界面——类似物 API 在范围较广的查询中会超时。

  3. 使用 RDKit 在本地筛选属性。 ZINC22 不支持在服务器端按 MW/logP/HBD 查询。先检索候选项,再用 RDKit 筛选——也可在此步骤应用 PAINS/Brenk 警示。

  4. 礼貌地轮询。 SMILES 查询是异步的;每隔几秒轮询一次 /search/result/{task},设置合理的超时时间,并处理 PROGRESS → SUCCESS 状态转换(以及 TimeoutError)。

  5. 按规范 SMILES 去重。 同一结构可能对应多个 ZINC ID/目录。对结构进行对接前,使用 RDKit 规范化(Chem.MolToSmiles(Chem.MolFromSmiles(smi)))。

  6. 通过 catalogs 验证是否可购买。 将 purchase: 1 的 catalogs 条目视为可订购;记录 supplier_code 和 catalog_name 以便采购。

  7. 缓存结果。 ZINC 数据会定期更新;使用带日期的文件名缓存物质/搜索 JSON,并避免在同一项目中重复查询。

常见用法

配方:通过 SMILES 查询 ZINC22 ID 和是否可购买
matches = smiles_lookup("CC(=O)Nc1ccc(O)cc1")   # acetaminophen
for m in matches:
    cats = m.get("catalogs", [])
    print(f"{m['zinc_id']} | MW {m['tranche_details']['mwt']:.1f} | "
          f"catalogs {len(cats)} | purchasable {bool(cats)}")
配方:批量解析 ZINC ID 列表
zinc_ids = ["ZINC000000029632", "ZINCbq0000001gLA"]
for zid in zinc_ids:
    try:
        c = get_substance(zid)
        print(f"{zid}: {c['smiles']}  MW {c['tranche_details']['mwt']:.1f}")
    except requests.HTTPError as e:
        print(f"{zid}: not found ({e.response.status_code})")
配方:化合物库的属性分布
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors

df = pd.read_csv("docking_library.smi", sep=" ", names=["smiles", "zinc_id"])
df["mw"] = df["smiles"].map(lambda s: Descriptors.MolWt(Chem.MolFromSmiles(s)))
df["logp"] = df["smiles"].map(lambda s: Descriptors.MolLogP(Chem.MolFromSmiles(s)))
print(f"Library size: {len(df)}")
print(df[["mw", "logp"]].describe())

故障排除

问题原因解决方案
请求 zinc15.docking.org 会重定向到 /captcha/ 或返回 403旧版 ZINC15 REST API 受 CAPTCHA 限制,无法通过脚本使用按此处所示,将所有调用切换到 cartblanche22.docking.org(ZINC22 / CartBlanche)
GET /substances/{id}.json(复数)返回 HTML路径错误——该路径会匹配 SPA 的兜底路由使用单数 /substance/{id}.json
端点返回 200,且 content-type: application/json,但响应正文是 HTMLCartBlanche 会为未知路由提供 SPA 外壳(其 content-type 具有误导性)检查响应正文能否实际解析为 JSON;只有文档中列出的端点会返回数据
POST /smiles.json 返回 {"task": ...},但你预期会得到结果SMILES 搜索是异步的轮询 GET /search/result/{task},直到 status == "SUCCESS"
400 No Valid SMILES, please try againSMILES 未通过验证(或请求格式错误)使用 RDKit 验证 SMILES;通过 multipart 表单字段 smiles/dist/adist/database 提交
成功的任务出现 KeyError: 'data' / 'zinc22'有匹配项时,result 是字典;没有匹配项时则是空列表处理这两种数据结构(参见 smiles_lookup 辅助函数)
类似物搜索(dist >= 1)超时或只返回完全匹配项共享集群上的搜索空间非常大使用 dist=0(精确匹配);如需相似度搜索,请使用 Smallworld 网页界面;使用 RDKit 在本地扩展类似物
/substance/random.json 返回 400;/substance/random/{n}.json 返回 {"status":"PENDING"}随机端点是异步的,且并非简单请求脚本无需使用此端点;改用已知的 ZINC ID
化合物 ID 返回 HTTP 404ID 拼写错误,或此版本中不存在该 ID验证 ZINC ID;对于旧版 ID,尝试使用 database="zinc20"

相关技能

  • rdkit-cheminformatics — 计算检索到的 ZINC 化合物的属性,并应用 PAINS/Brenk 过滤器(此处进行属性过滤时必需)
  • autodock-vina-docking — 使用导出的 ZINC SMILES/SDF 文件进行分子对接筛选
  • chembl-database-bioactivity — 获取 ZINC 虚拟筛选中发现的化合物的生物活性数据
  • drugbank-database-access — 已获批药物的结构和注释

参考资料

评论

支持 Markdown