第3章:蛋白质结构数据获取——从PDB数据库下载与解析
做蛋白配体预测,第一步就是搞到高质量的蛋白结构数据。说白了,没结构算个啥?
我当年第一次做对接时,满网找结构文件,结果下载回来的全是残缺的……后来才明白,PDB 数据库虽然开放,但数据质量参差不齐,下载与解析的细节直接决定后续管线的成败。这一章咱们就聊聊怎么科学地从 PDB 数据库往下扒数据,以及怎么把它变成程序能直接吃的东西。
3.1 PDB 数据库是什么?
PDB(Protein Data Bank)是目前全球最大的蛋白质结构数据库。你可以把它理解成结构生物学的「豆瓣」,只不过这里存的是原子坐标,而不是影评。
- 主要收录 X 射线晶体衍射、核磁共振(NMR)、冷冻电镜(Cryo-EM)解析的蛋白结构
- 每个结构有一个独一无二的 4 位 PDB ID(例如 1hiv、4pe8)
- 提供两种主流格式:传统 PDB 格式(.pdb)和新版 mmCIF 格式(.cif)——我个人建议尽量用 mmCIF,因为它能处理更大更完整的结构,不截断
你想想看,每天全球有上千个新结构上传,咱们的管线必须能自动识别并下载最新的数据。所以第一步就是学会用程序跟 PDB 服务器打招呼。
3.2 下载 PDB 文件:三种主流姿势
先上一张流程图,让你对整个数据获取流程有个整体印象:
图上这个流程,说白了就是三步走:有ID → 下文件 → 解析。但实际动手时,这里面全是细节。
3.2.1 方法一:直接 HTTP GET(适合小批量)
RCSB PDB 提供了一个非常直接的 RESTful 下载接口:
https://files.rcsb.org/download/{pdb_id}.pdb
https://files.rcsb.org/download/{pdb_id}.cif
用 Python 的 requests 库就能搞定:
import requests
def download_pdb(pdb_id, format='pdb'):
base_url = 'https://files.rcsb.org/download/'
url = f'{base_url}{pdb_id}.{format}'
resp = requests.get(url, timeout=30)
resp.raise_for_status()
with open(f'{pdb_id}.{format}', 'w') as f:
f.write(resp.text)
print(f'{pdb_id} 下载完成')
3.2.2 方法二:BioPython 的 PDBList(推荐批量下载)
BioPython 封装了一个超级好用的 PDBList 类,支持断点续传、自动重试:
from Bio.PDB.PDBList import PDBList
pdb_list = PDBList()
pdb_list.retrieve_pdb_file('4pe8', pdir='./structures', file_format='pdb')
# 或者用 mmCIF
pdb_list.retrieve_pdb_file('4pe8', pdir='./structures', file_format='mmCIF')
它内部会先访问 PDB 的 FTP 服务器,如果失败会回退到 HTTP。你想想看,BioPython 连这种边界情况都帮你想好了,比我们自己写一堆 try-except 省事多了。
3.2.3 方法三:PDBe 的 GraphQL API(面向现代应用)
如果你需要更细粒度的元数据(如分辨率、R-factor、配体列表),直接解析文件太慢。PDBe 提供了 GraphQL 接口:
POST https://www.ebi.ac.uk/pdbe/graphql/
Content-Type: application/json
{
"query": "{
entry(entry_id: \"4pe8\") {
resolution
r_free
ligand_ids
structure_determination_method
}
}"
}
返回 JSON,非常规整。适合在管线初期筛选结构质量。
3.3 解析 PDB 文件:从文本到数据结构
文件下载到手了,怎么把那些行列坐标提取成能算的矩阵?
3.3.1 手动解析 vs BioPython 解析
你要是新手,别自己写正则去抠 ATOM 行。我曾经犯过这个傻,写了两百行代码,结果遇到非标准格式的 PDB 文件就崩。老老实实用 BioPython 的 PDBParser:
from Bio.PDB.PDBParser import PDBParser
parser = PDBParser(QUIET=True) # QUIET 抑制警告
structure = parser.get_structure('4pe8', '4pe8.pdb')
# structure[0] 是第一个模型(model),大多数 X-ray 结构只有一个 model
model = structure[0]
# model['A'] 是链 A
chain = model['A']
# 遍历残基
for residue in chain:
res_id = residue.get_id()
res_name = residue.get_resname()
# 遍历原子
for atom in residue:
coord = atom.get_vector()
# coord 是 NumPy 风格的 Vector 对象,可直接转数组
mmCIF 格式解析更简单:
from Bio.PDB.MMCIFParser import MMCIFParser
parser = MMCIFParser(QUIET=True)
structure = parser.get_structure('4pe8', '4pe8.cif')
注意:mmCIF 解析出来的结构对象和 PDB 解析出来的一模一样,上层代码完全不用改——BioPython 做了统一抽象。
3.4 提取关键信息:残基与配体坐标
做蛋白配体预测,最关心的是配体结合位点的残基。我一般会这么处理:
- 先区分蛋白质残基和非蛋白质分子(水、离子、配体)
- 保留配体周围 8~10 Å 内的残基作为结合口袋
- 完整保存整个蛋白的坐标,用于后续对接或打分
代码示例——提取所有配体并计算其原子中心:
from Bio.PDB import Vector
def get_ligands(structure):
ligands = []
for model in structure:
for chain in model:
for residue in chain:
# 异质性残基(HETATM)通常是配体
if residue.get_id()[0] != ' ':
atoms = [atom.get_vector() for atom in residue]
center = sum(atoms, Vector(0,0,0)) / len(atoms)
ligands.append((residue.get_resname(), center))
return ligands
3.5 数据完整性与校验
结构下载下来,别急着往下传。至少检查这几样:
| 检查项 | 为什么要查 | 常用手段 |
|---|---|---|
| 分辨率 | 分辨率 > 3.5 Å 的结构通常位置不准 | 从 PDB 文件 REMARK 2 或 header 中解析 |
| 是否完整(无缺失残基) | 晶体结构常有 loop 缺失 | 比较序列长度与实测 CA 原子数 |
| 水分子是否过多 | 太多水会影响对接采样 | 计算水分子与总原子数比率 |
| 配体是否与论文一致 | 有的 PDB 配体是 buffer 分子 | 预先维护一个已知配体列表 |
举例,用 BioPython 读取分辨率:
structure = parser.get_structure('id', 'file.pdb')
resolution = structure.header['resolution']
# 或者从 mmCIF 的 _refine.ls_d_res_high 字段获取
3.6 批量下载与并发处理
管线上线后,每天要处理几百上千个结构,单线程太慢了。我习惯用 multiprocessing + PDBList:
from multiprocessing import Pool
from Bio.PDB.PDBList import PDBList
def download_one(pdb_id):
pl = PDBList()
pl.retrieve_pdb_file(pdb_id, pdir='./structures', file_format='mmCIF')
return pdb_id
with Pool(processes=8) as pool:
results = pool.map(download_one, pdb_id_list)
注意:PDBList 内部有连接池限制,用太多进程反而会触发限流。我试过 32 进程直接被 RCSB 封 IP 了……建议 4~8 个就够了。
3.7 小结:一条实用的下载解析管线
把这个片段整合一下,就是咱们管线里最常见的一段:
# 1. 输入 PDB ID 列表
pdb_ids = ['4pe8', '1hiv', '3cyx']
# 2. 下载 mmCIF 文件
pdb_list = PDBList()
for pid in pdb_ids:
pdb_list.retrieve_pdb_file(pid, pdir='./cif', file_format='mmCIF')
# 3. 解析并提取信息
parser = MMCIFParser(QUIET=True)
for pid in pdb_ids:
struct = parser.get_structure(pid, f'./cif/{pid}.cif')
# 打印基本信息
print(f'{pid}: {len(struct[0])} chains, resolution {struct.header["resolution"]}')
# 继续做口袋提取或其他处理
嗯,到这里,蛋白质结构数据的获取与解析就讲完了。实际操作中你会发现不同来源的文件格式差异很大(有些来自 PDB-REDO 的修正版,有些来自 AlphaFold 的预测结构),解析逻辑还要更灵活一些。但掌握了 PDB 数据库的下载规律和 BioPython 的核心用法,剩下的就是见招拆招了。