第4章 配体数据获取:从 PubChem、ChEMBL、ZINC 等数据库采集

配体数据是蛋白配体预测管线的「燃料」。没有高质量的小分子数据,模型训练就是空中楼阁。我个人习惯把这步叫做「数据采矿」——挖对了矿,后面建模省一大半功夫。

本章我会带你走一遍主流配体数据库的来源、采集方式和常见坑。你想想看,市面上 PubChem、ChEMBL、ZINC 这三个库各有脾气,有的偏药物相关,有的偏虚拟筛选,有的动不动几个亿化合物。怎么选?怎么下?怎么洗?嗯,这篇文章一次性讲透。

本章核心:掌握三大配体数据库的采集方法,学会用 API 和批量下载两种方式获取数据,并了解 SDF、SMILES、CSV 等格式的转换与清洗要点。

4.1 三大数据库初印象

先给个全景对比,你心里有个底。

数据库规模(化合物数)数据类型主打领域下载方式
PubChem1.2亿+生物活性、化学性质、文献链接综合化学/生物FTP批量、REST API、PUG REST
ChEMBL200万+药物活性数据(IC₅₀、Ki等)药物发现SQLite/CSV 下载、REST API
ZINC2.3亿+3D构象、理化性质、官方分类虚拟筛选文件分块、ZINC20 子集、API

我在项目里遇到最多的问题是:新手一股脑把 ZINC 的 2 亿分子全下了,本地硬盘爆了不说,里面大量无用分子(比如分子量太大、环系过多)反而拉低模型性能。所以先搞清楚需求再下手。

4.2 PubChem 采集实战

PubChem 数据量最大,API 也最良心。我一般用它的 PUG REST 接口,或者直接 FTP 拉 SDF 文件。下面给两种方式。

4.2.1 通过 PUG REST API 按 CID 列表抓取

假设你从文献里拿到一组 PubChem CID,想批量拿 SDF 或 SMILES。直接调接口:

# 获取单个 CID 的 SDF(用 wget 或 curl)
curl -O "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/2244/SDF"
# 2244 是阿司匹林的 CID,改成你自己的

# 批量获取 SMILES(POST 方式)
curl -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "cid=2244,2245,2246,2247&response_type=display" \
  "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/property/IsomericSMILES/TXT"

注意 PubChem API 有频率限制,每秒别超过 5 次。我之前写过一段脚本不加限制,结果被 ban 了半小时……嗯,学会加 sleep(0.2)。

4.2.2 FTP 批量下载全量 SDF

你需要几千万分子?FTP 是王道。

# 下载 PubChem 化合物 SDF 子集(约 100 万分子一个文件)
wget -r -np -nH --cut-dirs=3 \
  -A "*.sdf.gz" \
  ftp://ftp.ncbi.nlm.nih.gov/pubchem/Compound/CURRENT-Full/SDF/

这个路径下的文件按块分好,每个块 100 万分子,压缩后也有几百 MB。解压后导入数据库处理。

技巧:我会先用小样本(比如第一个块)测试洗数据流程,全部跑通再批量处理剩下的。否则处理 5 个小时发现清洗脚本有 bug……我曾经就这么干过。

4.3 ChEMBL 采集:药物活性数据的金矿

ChEMBL 的强项是标注活性值。我们需要的是靶点相关的小分子和它们的 IC₅₀/Ki 数据。ChEMBL 提供了很好的 SQLite 文件,我个人最喜欢这种方式。

4.3.1 下载 SQLite 数据库

# 直接下载最新版(约 2GB)
wget https://ftp.ebi.ac.uk/pub/databases/chembl/ChEMBLdb/latest/chembl_33.db.gz
gunzip chembl_33.db.gz

# 用 Python 连上直接查
import sqlite3
conn = sqlite3.connect("chembl_33.db")

# 查某个靶点(比如 EGFR,Uniprot: P00533)的活性数据
query = """
SELECT m.chembl_id, m.pref_name, a.standard_type,
       a.standard_value, a.standard_units, a.pchembl_value
FROM assays a
JOIN target_dictionary t ON a.tid = t.tid
JOIN molecule_dictionary m ON a.molregno = m.molregno
WHERE t.chembl_id = 'CHEMBL203'
  AND a.standard_type IN ('IC50', 'Ki')
  AND a.standard_units = 'nM'
"""
import pandas as pd
df = pd.read_sql_query(query, conn)
df.to_csv("EGFR_ligands.csv", index=False)

看,一行 SQL 就把 EGFR 的所有 nM 级活性化合物扒下来了。比去网页点下载舒服多了。

4.3.2 REST API 方式(适合小批量)

如果你只是验证几个分子,ChEMBL API 更快。

# 获取 CHEMBL203 的分子列表
curl "https://www.ebi.ac.uk/chembl/api/data/molecule?target_chembl_id=CHEMBL203&format=json"

不过 API 分页一次只返回 20 条,大批量数据还是 SQLite 爽。

注意:ChEMBL 数据库经常更新,老版本中的靶点名和标准化字段可能不一致。我建议你在代码里固定一个版本号,比如 chembl_33,避免结果不可复现。

4.4 ZINC 采集:虚拟筛选的庞然大物

ZINC 目前有超过 2.3 亿化合物,并且官方按「库」分了子集,比如 FDA 批准库、片段库、lead-like 库等。我最常取用的是「lead-like」和「drug-like」子集,因为物理化学性质更适合后续对接。

4.4.1 选择子集批量下载

ZINC 官网每一个子集都会提供文件列表。例如:

# 下载 lead-like 子集的所有 mol2 文件(每个文件 10 万个分子)
wget -i https://zinc20.docking.org/substances/subset/lead-like/sdf.flist

# 或者直接下载打包好的 tar.gz
wget https://zinc20.docking.org/substances/subset/lead-like.tar.gz
tar zxvf lead-like.tar.gz

这里有个坑:ZINC 文件很大,一个子集可能几十 GB。我建议你按需求只下载前几批。比如先取 100 万个分子训练模型,看效果再决定要不要加量。

4.4.2 用 API 获取单个分子 3D 结构

如果只是少量分子,ZINC 的 REST API 也支持:

# 根据 ZINC ID 取 mol2
curl "https://zinc20.docking.org/substances/ZINC000000000001.mol2"
核心提醒:ZINC 的 3D 构象已经是优化过的,做对接可以直接用。但注意文件格式是 mol2 或 SDF,你需要 rdkit 来转成标准输入格式。

4.5 数据清洗与整合

配体数据拿到手,不是直接用的。以下是我必做的几步:

  1. 去重:不同来源可能有同一个分子(InChI Key 做哈希)。
  2. 标准化:pH 7.4 下的质子化状态、去除盐、中性化。
  3. 过滤:按 Lipinski 五规则、PAINS 过滤、分子量范围等。
  4. 格式统一:全部转成 SMILES + ID 的 CSV,或者 SDF。
  5. 活性值归一化:ChEMBL 的数据 pChEMBL = -log10(IC50 M),统一到同一量纲。

我用 Python 写了一个小管线,放在下面供参考:

from rdkit import Chem
from rdkit.Chem import SaltRemover, Descriptors

def clean_and_filter(smiles, mol_id):
    mol = Chem.MolFromSmiles(smiles)
    if mol is None:
        return None
    # 去除盐
    remover = SaltRemover.SaltRemover()
    mol = remover.StripMol(mol)
    # 中性化(简单去电荷)
    [atom.SetFormalCharge(0) for atom in mol.GetAtoms()]
    # 过滤:200 < MW < 600, logP <= 5
    mw = Descriptors.ExactMolWt(mol)
    logp = Descriptors.MolLogP(mol)
    if mw < 200 or mw > 600 or logp > 5:
        return None
    return Chem.MolToSmiles(mol)

# 应用于 DataFrame
df['clean_smiles'] = df['smiles'].apply(lambda x: clean_and_filter(x, None))
避坑指南:我曾经信了某个数据集里的「标准 smiles」,结果直接用来做对接,发现全都是反式构象,被评委怼了一顿。所以务必将 SMILES 通过 rdkit 再标准化一次,包括芳香性表示、同位素去除等。

4.6 配体数据获取的整体流程

下面这张图总结了本章的知识体系,你可以对照着搭建自己的数据采集管线。

配体数据获取整体流程 PubChem 1.2亿+ 通用化合物 ChEMBL 200万+ 活性标注 ZINC 2.3亿+ 虚拟筛选 FTP / PUG REST SQLite / REST API 子集 tar.gz / API 去重 → 标准化 → 过滤 → 格式统一 InChI Key / pH 中性 / Lipinski / SMILES统一 本地配体库(SDF / CSV / SQLite) 建议先小规模验证清洗逻辑,再批量处理;同时记录源数据库版本号保证可复现

这个流程我实践过很多次,成功率几乎 100% 的关键就两点:

  • 版本锁定:数据库版本、API 日期、下载批次记下来,否则两个月后重跑发现数据不一致。
  • 增量更新:别每次全量下载。PubChem 每日更新,我只下更新日志里的 diff 文件。
重要警告:不要直接拿下载的原始文件训练模型。ZINC 的 mol2 文件里常含有带金属离子或者带电荷的原子,这些不经清洗会造成 false positives。我亲眼见过有人用未清洗的 ZINC 数据训练出一个对镁离子「高活性」的假模型……