第4章 配体数据获取:从 PubChem、ChEMBL、ZINC 等数据库采集
配体数据是蛋白配体预测管线的「燃料」。没有高质量的小分子数据,模型训练就是空中楼阁。我个人习惯把这步叫做「数据采矿」——挖对了矿,后面建模省一大半功夫。
本章我会带你走一遍主流配体数据库的来源、采集方式和常见坑。你想想看,市面上 PubChem、ChEMBL、ZINC 这三个库各有脾气,有的偏药物相关,有的偏虚拟筛选,有的动不动几个亿化合物。怎么选?怎么下?怎么洗?嗯,这篇文章一次性讲透。
4.1 三大数据库初印象
先给个全景对比,你心里有个底。
| 数据库 | 规模(化合物数) | 数据类型 | 主打领域 | 下载方式 |
|---|---|---|---|---|
| PubChem | 1.2亿+ | 生物活性、化学性质、文献链接 | 综合化学/生物 | FTP批量、REST API、PUG REST |
| ChEMBL | 200万+ | 药物活性数据(IC₅₀、Ki等) | 药物发现 | SQLite/CSV 下载、REST API |
| ZINC | 2.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。解压后导入数据库处理。
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 爽。
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"
4.5 数据清洗与整合
配体数据拿到手,不是直接用的。以下是我必做的几步:
- 去重:不同来源可能有同一个分子(InChI Key 做哈希)。
- 标准化:pH 7.4 下的质子化状态、去除盐、中性化。
- 过滤:按 Lipinski 五规则、PAINS 过滤、分子量范围等。
- 格式统一:全部转成 SMILES + ID 的 CSV,或者 SDF。
- 活性值归一化: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))
4.6 配体数据获取的整体流程
下面这张图总结了本章的知识体系,你可以对照着搭建自己的数据采集管线。
这个流程我实践过很多次,成功率几乎 100% 的关键就两点:
- 版本锁定:数据库版本、API 日期、下载批次记下来,否则两个月后重跑发现数据不一致。
- 增量更新:别每次全量下载。PubChem 每日更新,我只下更新日志里的 diff 文件。