分子数据获取:公共数据库下载与解析

做SMILES生成模型,第一步就是搞数据。说白了,没有数据,模型就是个空壳。我个人习惯先从公开数据库下手,比如ChEMBL、ZINC和PubChem。这三个库,基本上把已知的分子世界都覆盖了。

为什么一定要用公共数据库?

你想想看,自己搭建一个分子筛选平台来生成SMILES,成本有多高?我曾经在项目初期试过自己做湿实验收集数据,结果忙活两个月,才攒了不到一千个分子。后来转用公共数据库,一天就拿到了几百万。这效率差距,不是一星半点。

公共数据库的好处很明显:

  • 数据量大:动辄上亿个化合物,足够喂饱你的生成模型。
  • 格式统一:大部分提供SMILES、SDF、JSON等多种导出格式。
  • 可复现:你跑出来的结果,换个人也能从同一个源拉数据,对得上。
  • 带标注:不少分子还有生物活性、毒性、代谢路径等额外信息,做条件生成时特别好用。
一句话总结:公共数据库是SMILES生成模型的弹药库。没有弹药,模型再强也是白搭。

三大主流数据库:谁是谁?

这三个库,侧重点完全不同。我在刚开始接触时也搞混过,后来摸清楚门道了,给你们掰扯清楚。

数据库 规模(2024年数据) 核心数据格式 访问方式 主要用途
ChEMBL 约240万活性化合物 SMILES, SDF, CSV Web + API + FTP批量下载 药物研发、活性预测
ZINC 约2.3亿可购买化合物 SMILES, SDF, Mol2 Web + FTP + Transzinc工具 虚拟筛选、分子动力学
PubChem 约1.2亿化合物 SMILES, JSON, XML, SDF Web + PUG REST API + FTP 多学科、化合物信息检索
我建议:如果你做药物相关,先抓ChEMBL;如果要做虚拟筛选,ZINC的"drug-like"子集很香;PubChem最适合做大规模检索和跨库对比。

从ChEMBL抓数据:我常用的路子

ChEMBL是欧洲生物信息学研究所维护的,质量很高。我一般直接去FTP下载完整版,然后本地建索引。别每次都调API,几千几万个分子时,API请求容易超时。

方法一:FTP批量下载

ChEMBL每隔几个月就会发布一次完整快照。下载命令很简单:

wget -r -np -nH --cut-dirs=5 -R index.html \
    ftp://ftp.ebi.ac.uk/pub/databases/chembl/ChEMBLdb/latest/

下载下来是一个SDF文件,解压后大概1-2GB。嗯,这里要注意,这个SDF文件里包含了所有活性数据、结构、靶点信息。如果你只想要SMILES,可以用RDKit过滤一下。

方法二:用Python实现自动下载与解析

我个人更喜欢直接写脚本控制。用chembl_webresource_client库,从API拉数据,边拉边存。

from chembl_webresource_client.new_client import new_client

molecule = new_client.molecule
results = molecule.filter(molecule_properties__mw__gte=300,
                          molecule_properties__mw__lte=500,
                          molecule_properties__alogp__lte=5).only(
    ['molecule_chembl_id', 'molecule_structures'])

smiles_list = []
for mol in results:
    if 'canonical_smiles' in mol['molecule_structures']:
        smiles_list.append(mol['molecule_structures']['canonical_smiles'])
        
print(f"共获取 {len(smiles_list)} 条SMILES")
with open('chembl_smiles.txt', 'w') as f:
    for smi in smiles_list:
        f.write(smi + '\n')
警告:API有速率限制。我在项目中就吃过亏,一次性请求太多,直接被封IP。建议每次控制几千条,加一个time.sleep(1)。

ZINC数据下载:量最大但有点“脏”

ZINC数据库最大的特点就是全。2.3亿个化合物,而且都是可以购买的。缺点是数据质量参差不齐,有些SMILES里的原子类型不标准。我曾经从ZINC下载了一批数据,结果训练模型时一直不收敛,最后发现是SMILES里有“*”通配符,模型学歪了。

子集选择是关键

ZINC按不同的过滤规则分了子集,下载前想清楚你要什么:

  • ZINC20_clean:约2亿个,标准格式,去除了盐和溶剂
  • ZINC20_in-stock:约2300万个,表示可以买到的
  • ZINC20_drug-like:约1300万个,符合类药五规则
  • ZINC20_fragment:约1.4亿个,分子量≤250

我一般用Transzinc工具来下载,支持多线程,比FTP快多了。

transzinc --subset drug-like --format smi --output zin_druglike.smi

输出是标准的SMILES文件,每行一个分子。

解析ZINC的SMILES

ZINC的SMILES文件通常比较大,几十GB。我习惯用pandas分批读取,然后做一些基本清洗。

import pandas as pd

# 读取SMILES文件,假设第一列是ZINC ID,第二列是SMILES
chunks = pd.read_csv('zin_druglike.smi', 
                     sep='\t', 
                     header=None, 
                     names=['zinc_id', 'smiles'],
                     chunksize=100000)

valid_smiles = []
for chunk in chunks:
    # 简单过滤:长度小于50,且不含特殊字符
    mask = (chunk['smiles'].str.len() < 50) & \
           (~chunk['smiles'].str.contains(r'[\*\.\[\]]', na=False))
    valid_smiles.extend(chunk[mask]['smiles'].tolist())
    
print(f"清洗后剩余 {len(valid_smiles)} 条SMILES")
记住:ZINC数据集下载后一定要做一次SMILES标准化。我在第一个项目里因为忽略了这一步,模型生成的很多分子在化学上根本不成立。

PubChem:最全的“化合物百科全书”

PubChem的优势是跨学科。它不止有化合物,还有基因、蛋白、疾病关联。如果你做的是多模态生成或者条件生成,PubChem的丰富标注数据特别宝贵。

PUG REST API使用

PubChem的API设计得很规范,能直接查询化合物的SMILES。

import requests

def get_smiles_from_pubchem(cid):
    """从PubChem获取CID对应的SMILES"""
    url = f"https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/{cid}/property/CanonicalSMILES/JSON"
    try:
        r = requests.get(url, timeout=10)
        if r.status_code == 200:
            data = r.json()
            return data['PropertyTable']['Properties'][0]['CanonicalSMILES']
    except Exception as e:
        print(f"请求CID {cid} 失败: {e}")
        return None

# 示例:获取阿司匹林(CID=2244)的SMILES
smiles = get_smiles_from_pubchem(2244)
print(smiles)  # 输出: CC(=O)Oc1ccccc1C(=O)O
技巧:如果一次性需要很多CID,用POST批量请求。我一般用一次处理100个CID,又快又稳定。

PubChem FTP下载

如果你要拉全库,FTP是最靠谱的。PubChem的FTP结构很清晰:

# 下载整个化合物的SMILES文件(约3GB压缩)
wget ftp://ftp.ncbi.nlm.nih.gov/pubchem/Compound/Extras/CID-SMILES.gz
gzip -d CID-SMILES.gz

# 查看格式:每行一个CID和SMILES
# 2244	CC(=O)Oc1ccccc1C(=O)O
# 76175	CC(C)CC1=CC=C(C=C1)C(C)C(=O)O

下载后,我会用awk或者Python快速筛选:比如只保留分子量在200-500之间的。

三种数据下载方式的对比

我个人在实际项目中,会根据情况选不同的方式。给你列个表,方便做技术选型:

下载方式 适用场景 数据量级 实时性 网络要求
Web页面导出 少量样本测试,验证思路 <1000条 实时 低
REST API 有编程接口,可批量查询 1K-100K条 准实时 中
FTP/Transzinc批量 全量或大子集下载 百万-亿级 快照版 高
避坑:FTP下载一定要用断点续传。我之前下载ZINC全库时,网络断了一次,又从头拉,白白浪费了6个小时。加上-c参数,血泪教训。

解析三大数据库的核心流程

不管你从哪个库下载数据,解析的流程基本一致。我总结了一个通用流程,你们可以拿着直接用:

分子数据获取与解析流程 从公共数据库获取SMILES的标准流程 ChEMBL ZINC PubChem 下载:FTP / API / Web → SDF / SMILES / JSON 清洗:去重、过滤盐溶剂、标准化SMILES 提取SMILES序列,生成训练集 划分:训练集 / 验证集 / 测试集 标准SMILES数据集 → 模型输入 注意:解析后做好质量检查

常用解析工具与库

解析SMILES时,有几个库是绕不开的。我用过不少,最顺手的给你列出来:

  • RDKit:化学信息学的瑞士军刀。解析、标准化、可视化一条龙。我99%的场景用它。
  • Open Babel:格式转换神器,能将SDF转为SMILES,还能批量处理。
  • ChemDataExtractor:挖论文里SMILES用的,做多源数据合并时帮了大忙。
  • pybel:Open Babel的Python封装,写脚本时比命令行友好很多。
核心工具推荐:RDKit + pandas 组合。RDKit负责化学逻辑,pandas负责数据管理。这两个配合,搞定90%的数据处理需求。

一个完整的解析示例

我来演示一下,从SDF文件解析SMILES并做初步清洗的全过程。这是我项目里几乎每天都会用的脚本模板。

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

def parse_sdf_to_smiles(sdf_path, output_path):
    """从SDF文件提取SMILES,并做过滤"""
    
    supplier = Chem.SDMolSupplier(sdf_path)
    remover = SaltRemover.SaltRemover()
    
    data = []
    for idx, mol in enumerate(supplier):
        if mol is None:
            continue
            
        # 去除盐和溶剂
        mol_no_salt = remover.StripMol(mol, dontRemoveEverything=True)
        
        # 获取SMILES
        smiles = Chem.MolToSmiles(mol_no_salt, canonical=True)
        
        # 简单过滤
        mw = Descriptors.MolWt(mol_no_salt)
        if mw < 150 or mw > 600:
            continue
            
        # 检查SMILES是否合法
        try:
            check = Chem.MolFromSmiles(smiles)
            if check is None:
                continue
        except:
            continue
            
        data.append({
            'smiles': smiles,
            'mol_weight': round(mw, 2),
            'num_atoms': mol_no_salt.GetNumAtoms()
        })
    
    df = pd.DataFrame(data)
    df.to_csv(output_path, index=False)
    print(f"写入 {len(df)} 条SMILES到 {output_path}")
    return df

# 使用
df_smiles = parse_sdf_to_smiles('chembl_32.sdf', 'clean_smiles.csv')
个人小习惯:解析完之后,随机抽100条,手动检查一下SMILES是不是正常。有时候数据格式问题只有肉眼才看得出来。

常见问题与避坑指南

做数据获取这几年,踩过不少坑。分享几个典型的:

  • SMILES不唯一:同一个分子在不同数据库里的SMILES写法可能不同。解决方法是统一用RDKit做canonical(规范)转换。
  • 特殊符号问题:ZINC里经常出现“*”、“[”这些通配符,训练模型前一定要过滤掉。
  • 数据不平衡:ChEMBL偏重药物分子,分子量集中在250-500。如果你要生成小分子或大分子,需要额外补充数据源。
  • 网络中断:下载大文件时,一定要做重试机制。我用的脚本里都加了三次重试。
切记:不要直接拿原始数据训练模型。我在第一个版本的生成模型里,直接把下载的ZINC数据喂进去,结果生成了很多带“*”的无效分子。数据清洗这一步,省不了。

好了,以上就是从三个公共数据库获取并解析SMILES的全部内容。说白了,数据质量决定了模型效果的天花板。花时间把这块做好,后面训练模型时会顺很多。