关于 pdb-database
这份技能指南通过 RCSB PDB 的公开 REST 和 GraphQL 接口访问生物大分子结构数据,面向结构生物学与药物发现工作。它涵盖结构搜索、元数据获取和坐标文件下载,无需 API 密钥。
主要功能
- 01按文本和属性搜索结构
- 02进行序列与三维结构相似性搜索
- 03通过 GraphQL 获取结构元数据
- 04下载 PDB、mmCIF 和 FASTA 文件
- 05组合查询并批量分页获取结果
适用场景
- 01寻找带特定属性的蛋白质结构
- 02比较同源蛋白的结构记录
- 03收集药物靶点结构及配体信息
- 04下载结构文件用于对接或分子动力学
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill pdb-database安装后在 Claude Code 中输入/pdb-database确认已加载。
详细介绍
PDB 数据库
为什么没有 SDK?
rcsb-apiPython SDK 只是对三个公开、无需身份验证的 REST 端点(search.rcsb.org、data.rcsb.org、files.rcsb.org)的便捷封装。SDK 不可用时,每项操作都可以通过普通的requests和一个小型 JSON 负载来复现。本 SKILL.md 全程使用 REST 方式,因此只要环境中安装了requests,代码就能运行。
概述
RCSB PDB 是全球生物大分子三维结构数据的存储库,收录了 200,000 多个通过实验测定的结构。可通过三个免费、无需身份验证的端点进行编程访问:
| API | 基础 URL | 方法 | 用途 |
|---|---|---|---|
| Search | https://search.rcsb.org/rcsbsearch/v2/query | POST JSON | 通过文本、属性筛选条件、序列或三维相似性查找 PDB ID |
| Data | https://data.rcsb.org/graphql | POST GraphQL | 获取结构化元数据(条目、聚合物实体、装配体、配体) |
| Files | https://files.rcsb.org/download/{id}.{format} | GET | 下载坐标文件(mmCIF、PDB、FASTA) |
在进行程序化结构生物学查询、药物靶点分析和蛋白质家族比较时,使用此技能。
适用场景
- 按关键词或属性搜索蛋白质或核酸的晶体/冷冻电镜/NMR 结构
- 查找与查询序列(MMseqs2)或三维几何形状(BioZernike)相似的结构
- 获取结构集合的实验元数据(分辨率、方法、生物体、提交日期)
- 下载用于分子动力学、对接或可视化的坐标文件(PDB、mmCIF)
- 为机器学习或药物发现工作流构建基于结构的数据集
- 比较同一靶点家族中蛋白质-配体复合物
- 对于 AlphaFold 预测的结构,请改用
alphafold-database-access - 对于不涉及结构的蛋白质序列/注释查询,请改用
uniprot-protein-database
先决条件
- Python 包:
requests(唯一必需项)。可选:使用biopython解析下载的坐标文件。 - 无需 API 密钥:RCSB PDB 可免费访问。
- 速率限制:未公布硬性限制。在请求之间适当延迟
time.sleep(0.2-0.5)即可;遇到 HTTP 429 时实施指数退避。
pip install requests
# Optional, for coordinate parsing:
pip install biopython
快速开始
典型的先搜索、再获取模式:调用 Search API 获取 PDB ID 列表,然后通过 GraphQL Data API 查询元数据。
import requests
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
DATA = "https://data.rcsb.org/graphql"
# 1. Search: human X-ray structures of "kinase" at resolution < 2.0 Å
payload = {
"query": {
"type": "group", "logical_operator": "and",
"nodes": [
{"type": "terminal", "service": "full_text",
"parameters": {"value": "kinase"}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entity_source_organism.scientific_name",
"operator": "exact_match", "value": "Homo sapiens"}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entry_info.resolution_combined",
"operator": "less", "value": 2.0}},
],
},
"return_type": "entry",
"request_options": {"paginate": {"rows": 10}},
}
r = requests.post(SEARCH, json=payload, timeout=30)
r.raise_for_status()
result = r.json()
pdb_ids = [hit["identifier"] for hit in result["result_set"]]
print(f"Total matches: {result['total_count']}, first batch: {pdb_ids}")
# 2. Fetch metadata for the first hit via GraphQL
gql = """{ entry(entry_id: "%s") {
struct { title }
exptl { method }
rcsb_entry_info { resolution_combined deposited_atom_count polymer_entity_count }
} }""" % pdb_ids[0]
r2 = requests.post(DATA, json={"query": gql}, timeout=30)
entry = r2.json()["data"]["entry"]
print(entry["struct"]["title"])
print(f"Method: {entry['exptl'][0]['method']}, Resolution: {entry['rcsb_entry_info']['resolution_combined']} Å")
核心 API
模块 1:文本和属性搜索
全文搜索使用 service: "full_text",并会搜索所有已索引字段。
import requests
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
def text_search(keyword, rows=25):
payload = {
"query": {"type": "terminal", "service": "full_text",
"parameters": {"value": keyword}},
"return_type": "entry",
"request_options": {"paginate": {"rows": rows}},
}
r = requests.post(SEARCH, json=payload, timeout=30)
r.raise_for_status()
data = r.json()
return [hit["identifier"] for hit in data["result_set"]], data["total_count"]
ids, total = text_search("hemoglobin")
print(f"Found {total} structures; first batch: {ids[:5]}")
属性搜索使用 service: "text",并搭配结构化的 attribute/operator/value 参数。
import requests
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
def attribute_search(attribute, operator, value, return_type="entry", rows=25):
payload = {
"query": {"type": "terminal", "service": "text",
"parameters": {"attribute": attribute,
"operator": operator,
"value": value}},
"return_type": return_type,
"request_options": {"paginate": {"rows": rows}},
}
r = requests.post(SEARCH, json=payload, timeout=30)
r.raise_for_status()
return r.json()
# Human proteins
human = attribute_search("rcsb_entity_source_organism.scientific_name",
"exact_match", "Homo sapiens", rows=5)
print(f"Human structures: {human['total_count']}")
# X-ray only
xray = attribute_search("exptl.method", "exact_match", "X-RAY DIFFRACTION", rows=5)
print(f"X-ray structures: {xray['total_count']}")
# Resolution range: 1.5–2.5 Å
res = attribute_search(
"rcsb_entry_info.resolution_combined", "range",
{"from": 1.5, "to": 2.5, "include_lower": True, "include_upper": True},
rows=5
)
print(f"1.5–2.5 Å: {res['total_count']}")
模块 2:序列相似性搜索
使用 MMseqs2 查找序列相似的结构。服务为 "sequence";target 用于选择蛋白质或核酸。
import requests
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
kras_seq = ("MTEYKLVVVGAGGVGKSALTIQLIQNHFVDEYDPTIEDSYRKQVVIDGETCLLDILDTAGQ"
"EEYSAMRDQYMRTGEGFLCVFAINNTKSFEDIHHYREQIKRVKDSEDVPMVLVGNKCDLPS"
"RTVDTKQAQDLARSYGIPFIETSAKTRQGVDDAFYTLVREIRKHKEKMSK")
payload = {
"query": {
"type": "terminal", "service": "sequence",
"parameters": {
"target": "pdb_protein_sequence", # or "pdb_dna_sequence", "pdb_rna_sequence"
"value": kras_seq,
"evalue_cutoff": 0.1,
"identity_cutoff": 0.9,
},
},
"return_type": "polymer_entity",
"request_options": {"paginate": {"rows": 10}},
}
r = requests.post(SEARCH, json=payload, timeout=30)
r.raise_for_status()
data = r.json()
print(f"KRAS-like hits: {data['total_count']}")
for hit in data["result_set"][:5]:
print(f" {hit['identifier']} score={hit.get('score', 'n/a')}")
模块 3:结构相似性搜索
使用 BioZernike 描述符查找三维几何形状相似的结构。服务为 "structure";传入参考条目和装配体 ID。
import requests
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
payload = {
"query": {
"type": "terminal", "service": "structure",
"parameters": {
"value": {"entry_id": "4HHB", "assembly_id": "1"},
"operator": "strict_shape_match", # or "relaxed_shape_match"
},
},
"return_type": "polymer_entity",
"request_options": {"paginate": {"rows": 10}},
}
r = requests.post(SEARCH, json=payload, timeout=30)
r.raise_for_status()
data = r.json()
print(f"Structurally similar to 4HHB: {data['total_count']}")
for hit in data["result_set"][:5]:
print(f" {hit['identifier']} score={hit.get('score', 'n/a')}")
模块 4:数据检索(GraphQL)
data.rcsb.org/graphql 上的 GraphQL 端点是获取已知 PDB ID 结构化元数据的标准方式。一次请求即可提取完整数据层级(条目 → 聚合物实体 → 装配体 → chem_comp)中的字段。
import requests
DATA = "https://data.rcsb.org/graphql"
# Entry-level metadata
gql = """{ entry(entry_id: "4HHB") {
struct { title }
exptl { method }
rcsb_entry_info { resolution_combined deposited_atom_count polymer_entity_count nonpolymer_entity_count }
rcsb_accession_info { deposit_date initial_release_date }
} }"""
r = requests.post(DATA, json={"query": gql}, timeout=30)
entry = r.json()["data"]["entry"]
print(f"Title : {entry['struct']['title']}")
print(f"Method : {entry['exptl'][0]['method']}")
print(f"Resolution : {entry['rcsb_entry_info']['resolution_combined']} Å")
print(f"Atoms : {entry['rcsb_entry_info']['deposited_atom_count']}")
# Polymer entity (sequence, organism, MW)
gql = """{ polymer_entity(entry_id: "4HHB", entity_id: "1") {
entity_poly { pdbx_seq_one_letter_code }
rcsb_polymer_entity { formula_weight }
rcsb_entity_source_organism { scientific_name ncbi_taxonomy_id }
} }"""
r = requests.post(DATA, json={"query": gql}, timeout=30)
pe = r.json()["data"]["polymer_entity"]
print(f"Sequence (first 50): {pe['entity_poly']['pdbx_seq_one_letter_code'][:50]}")
print(f"Organism : {pe['rcsb_entity_source_organism'][0]['scientific_name']}")
print(f"MW : {pe['rcsb_polymer_entity']['formula_weight']}")
# Batch: pull metadata for many entries in one request
gql = """{ entries(entry_ids: ["4HHB", "1A3N", "1HHB"]) {
rcsb_id
struct { title }
exptl { method }
rcsb_entry_info { resolution_combined }
} }"""
r = requests.post(DATA, json={"query": gql}, timeout=30)
for e in r.json()["data"]["entries"]:
res = e["rcsb_entry_info"]["resolution_combined"]
print(f" {e['rcsb_id']}: {e['exptl'][0]['method']:<25} {res} Å — {e['struct']['title'][:40]}")
模块 5:文件下载
坐标文件(mmCIF、PDB、FASTA、装配体变体)可直接从 files.rcsb.org 获取。
import requests
def download_structure(pdb_id, fmt="cif", output_dir="."):
"""Download mmCIF / PDB / FASTA. URLs: .pdb, .cif, /fasta/entry/{ID}, .pdb1 (assembly)."""
url = f"https://files.rcsb.org/download/{pdb_id}.{fmt}"
r = requests.get(url, timeout=60)
if r.status_code == 200:
path = f"{output_dir}/{pdb_id}.{fmt}"
# mmCIF / PDB are text; assemblies and biological units are also text
with open(path, "w") as f:
f.write(r.text)
print(f"Downloaded {path} ({len(r.text)/1024:.1f} KB)")
return path
print(f"HTTP {r.status_code} for {pdb_id}.{fmt}")
return None
download_structure("4HHB", fmt="cif")
download_structure("4HHB", fmt="pdb")
# FASTA sequence for an entry
r = requests.get("https://www.rcsb.org/fasta/entry/4HHB", timeout=30)
r.raise_for_status()
print(r.text[:400])
模块 6:查询组合(group + logical_operator)
使用 type: "group" 和 "and"/"or" 形式的 logical_operator 组合终端查询。嵌套分组可以构造任意布尔表达式;否定通过在分组上使用 "operator": "negate" 的 "node_id" 引用实现(较少使用——通常会用反向属性筛选条件表示)。
import requests, datetime
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
# AND: high-resolution human structures
q_and = {
"type": "group", "logical_operator": "and",
"nodes": [
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entity_source_organism.scientific_name",
"operator": "exact_match", "value": "Homo sapiens"}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entry_info.resolution_combined",
"operator": "less", "value": 2.0}},
],
}
# OR: human or mouse
q_or = {
"type": "group", "logical_operator": "or",
"nodes": [
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entity_source_organism.scientific_name",
"operator": "exact_match", "value": "Homo sapiens"}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entity_source_organism.scientific_name",
"operator": "exact_match", "value": "Mus musculus"}},
],
}
# Combined: recent (last 30 days) + high-quality
one_month_ago = (datetime.date.today() - datetime.timedelta(days=30)).isoformat()
today = datetime.date.today().isoformat()
q_recent_hq = {
"type": "group", "logical_operator": "and",
"nodes": [
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entry_info.resolution_combined",
"operator": "less", "value": 2.0}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "refine.ls_R_factor_R_free",
"operator": "less", "value": 0.25}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_accession_info.initial_release_date",
"operator": "range",
"value": {"from": one_month_ago, "to": today,
"include_lower": True, "include_upper": True}}},
],
}
payload = {"query": q_recent_hq, "return_type": "entry",
"request_options": {"paginate": {"rows": 5}}}
r = requests.post(SEARCH, json=payload, timeout=30)
print(f"Recent high-quality: {r.json()['total_count']} structures")
模块 7:分页 + 批处理与速率限制
搜索响应包含 total_count。使用 request_options.paginate.start 和 rows 分页(实际使用中每页最多约 10000 条;100–500 条是合适的批次大小)。
import requests, time
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
def search_all(query_node, return_type="entry", page=100, max_results=None, delay=0.3):
"""Paginate through every result; rate-limit between pages."""
out, start = [], 0
while True:
payload = {"query": query_node, "return_type": return_type,
"request_options": {"paginate": {"start": start, "rows": page}}}
r = requests.post(SEARCH, json=payload, timeout=30)
r.raise_for_status()
d = r.json()
batch = [h["identifier"] for h in d.get("result_set", [])]
if not batch:
break
out.extend(batch)
if max_results and len(out) >= max_results:
return out[:max_results]
if len(batch) < page or len(out) >= d.get("total_count", 0):
break
start += page
time.sleep(delay)
return out
# Example: every insulin entry
ids = search_all(
{"type": "terminal", "service": "full_text", "parameters": {"value": "insulin"}},
max_results=300,
)
print(f"Insulin entries collected: {len(ids)}")
# Batch metadata fetch via GraphQL `entries(...)` to avoid one round-trip per ID
DATA = "https://data.rcsb.org/graphql"
def batch_metadata(pdb_ids, chunk=50):
"""Fetch (title, method, resolution) for many entries with one POST per chunk."""
all_rows = []
for i in range(0, len(pdb_ids), chunk):
ids_arr = pdb_ids[i:i+chunk]
ids_str = ", ".join(f'"{p}"' for p in ids_arr)
gql = f"""{{ entries(entry_ids: [{ids_str}]) {{
rcsb_id
struct {{ title }}
exptl {{ method }}
rcsb_entry_info {{ resolution_combined }}
}} }}"""
r = requests.post(DATA, json={"query": gql}, timeout=60)
r.raise_for_status()
for e in r.json()["data"]["entries"]:
res = e["rcsb_entry_info"]["resolution_combined"]
all_rows.append({
"pdb_id": e["rcsb_id"],
"method": e["exptl"][0]["method"] if e["exptl"] else None,
"resolution": res[0] if isinstance(res, list) and res else res,
"title": e["struct"]["title"],
})
return all_rows
rows = batch_metadata(ids[:20])
for r in rows[:5]:
print(f" {r['pdb_id']}: {r['method']:<25} {r['resolution']} Å — {r['title'][:50]}")
核心概念
搜索服务速查表
| 服务 | 使用场景 | 必需参数 |
|---|---|---|
full_text | 在所有已索引字段中进行自由文本关键词搜索 | value(字符串) |
text | 结构化属性筛选 | attribute、operator、value |
sequence | MMseqs2 序列相似性搜索 | target ∈ {pdb_protein_sequence、pdb_dna_sequence、pdb_rna_sequence},value(序列)、evalue_cutoff、identity_cutoff |
seqmotif | 模式 / 正则表达式 / PROSITE 模体 | value(模式)、pattern_type ∈ {simple、prosite、regex} |
structure | 3D 形状相似性(BioZernike) | value({entry_id, assembly_id})、operator ∈ {strict_shape_match、relaxed_shape_match} |
strucmotif | 3D 残基排列模体 | value(残基列表)、rmsd_cutoff |
chemical | 通过 SMILES/InChI 进行配体相似性搜索 | value、match_type ∈ {graph-exact、graph-relaxed、fingerprint-similarity、sub-structure-stereo-relaxed} |
AttributeQuery 运算符(服务:"text")
| 运算符 | 值的形式 | 示例 |
|---|---|---|
exact_match | 字符串 | "Homo sapiens" |
contains_words / contains_phrase | 字符串 | "tyrosine kinase" |
equals / greater / less / greater_or_equal / less_or_equal | 数字 | 2.0 |
range | {from, to, include_lower, include_upper} | {"from": 1.5, "to": 2.5, "include_lower": True, "include_upper": True} |
exists | (无) | — |
in | 数组 | ["X-RAY DIFFRACTION", "ELECTRON MICROSCOPY"] |
返回类型
return_type 控制 result_set 中标识符的粒度:
return_type | 标识符形式 | 示例 |
|---|---|---|
entry | 4HHB | 每个 PDB ID 一个 |
polymer_entity | 4HHB_1 | 每个聚合物链实体一个 |
non_polymer_entity | 4HHB_2 | 配体、辅因子 |
assembly | 4HHB-1 | 生物学单元 |
polymer_instance | 4HHB.A | 单条链的坐标 |
mol_definition | HEM | 化学组分(PDB 配体代码) |
Common Data API GraphQL 根字段
| 根字段 | 标识符形式 | 返回内容 |
|---|---|---|
entry(entry_id: ...) | "4HHB" | 条目级元数据 |
entries(entry_ids: [...]) | 数组 | 批量查询条目 |
polymer_entity(entry_id: ..., entity_id: ...) | "4HHB"、"1" | 序列 + 生物体 |
polymer_entity_instance(entry_id: ..., asym_id: ...) | "4HHB"、"A" | 链级坐标/元数据 |
assembly(entry_id: ..., assembly_id: ...) | "4HHB"、"1" | 生物学组装体 |
chem_comp(comp_id: ...) | "HEM" | 小分子参考信息 |
文件格式
| 格式 | URL 模式 | 备注 |
|---|---|---|
| mmCIF | https://files.rcsb.org/download/{id}.cif | 推荐;没有原子数量限制 |
| PDB | https://files.rcsb.org/download/{id}.pdb | 旧格式;原子数量上限为 99,999 |
| Assembly(mmCIF) | https://files.rcsb.org/download/{id}-assembly{N}.cif | 生物学单元 |
| FASTA | https://www.rcsb.org/fasta/entry/{id} | 仅序列 |
常见工作流
工作流 1:药物靶点结构集
目标:查找分辨率高且结合了配体的人类 EGFR 结构。
import requests, time
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
DATA = "https://data.rcsb.org/graphql"
payload = {
"query": {
"type": "group", "logical_operator": "and",
"nodes": [
{"type": "terminal", "service": "full_text",
"parameters": {"value": "EGFR epidermal growth factor receptor"}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entity_source_organism.scientific_name",
"operator": "exact_match", "value": "Homo sapiens"}},
{"type": "terminal", "service": "text",
"parameters": {"attribute": "rcsb_entry_info.resolution_combined",
"operator": "less", "value": 2.5}},
],
},
"return_type": "entry",
"request_options": {"paginate": {"rows": 50}},
}
r = requests.post(SEARCH, json=payload, timeout=30)
r.raise_for_status()
pdb_ids = [h["identifier"] for h in r.json()["result_set"]]
print(f"EGFR ≤2.5 Å human structures: {len(pdb_ids)}")
# Filter to entries with bound ligands via batch GraphQL
ids_str = ", ".join(f'"{p}"' for p in pdb_ids[:20])
gql = f"""{{ entries(entry_ids: [{ids_str}]) {{
rcsb_id
struct {{ title }}
rcsb_entry_info {{ resolution_combined nonpolymer_entity_count }}
}} }}"""
r2 = requests.post(DATA, json={"query": gql}, timeout=60)
for e in r2.json()["data"]["entries"]:
n_lig = e["rcsb_entry_info"]["nonpolymer_entity_count"] or 0
if n_lig > 0:
res = e["rcsb_entry_info"]["resolution_combined"]
res_v = res[0] if isinstance(res, list) else res
print(f" {e['rcsb_id']}: {res_v} Å, ligands={n_lig} — {e['struct']['title'][:60]}")
time.sleep(0.05)
工作流 2:蛋白质家族 — 序列相似结构
目标:查找所有序列与查询序列(KRAS)相似的 PDB 结构,然后汇总其分辨率和实验方法。
import requests, time
SEARCH = "https://search.rcsb.org/rcsbsearch/v2/query"
DATA = "https://data.rcsb.org/graphql"
kras_seq = ("MTEYKLVVVGAGGVGKSALTIQLIQNHFVDEYDPTIEDSYRKQVVIDGETCLLDILDTAGQ"
"EEYSAMRDQYMRTGEGFLCVFAINNTKSFEDIHHYREQIKRVKDSEDVPMVLVGNKCDLPS"
"RTVDTKQAQDLARSYGIPFIETSAKTRQGVDDAFYTLVREIRKHKEKMSK")
payload = {
"query": {
"type": "terminal", "service": "sequence",
"parameters": {"target": "pdb_protein_sequence", "value": kras_seq,
"evalue_cutoff": 1e-5, "identity_cutoff": 0.5},
},
"return_type": "polymer_entity",
"request_options": {"paginate": {"rows": 20}},
}
r = requests.post(SEARCH, json=payload, timeout=30)
hits = r.json()["result_set"]
print(f"KRAS family hits: {len(hits)}")
# Unique PDB IDs from polymer_entity identifiers (e.g., "4OBE_1" -> "4OBE")
entry_ids = sorted({h["identifier"].split("_")[0] for h in hits})
# Batch metadata
ids_str = ", ".join(f'"{p}"' for p in entry_ids)
gql = f"""{{ entries(entry_ids: [{ids_str}]) {{
rcsb_id
struct {{ title }}
exptl {{ method }}
rcsb_entry_info {{ resolution_combined }}
}} }}"""
r2 = requests.post(DATA, json={"query": gql}, timeout=60)
for e in sorted(r2.json()["data"]["entries"],
key=lambda x: (x["rcsb_entry_info"]["resolution_combined"] or [99])[0] if isinstance(x["rcsb_entry_info"]["resolution_combined"], list) else (x["rcsb_entry_info"]["resolution_combined"] or 99)):
res = e["rcsb_entry_info"]["resolution_combined"]
res_v = res[0] if isinstance(res, list) else res
print(f" {e['rcsb_id']}: {res_v} Å {e['exptl'][0]['method']:<25} {e['struct']['title'][:50]}")
工作流 3:使用 BioPython 下载 + 解析
目标:下载 mmCIF,然后使用 BioPython 枚举链。
import requests
from Bio.PDB import MMCIFParser
pdb_id = "4HHB"
r = requests.get(f"https://files.rcsb.org/download/{pdb_id}.cif", timeout=60)
r.raise_for_status()
with open(f"{pdb_id}.cif", "w") as f:
f.write(r.text)
parser = MMCIFParser(QUIET=True)
structure = parser.get_structure(pdb_id, f"{pdb_id}.cif")
for model in structure:
for chain in model:
std_res = [r for r in chain if r.id[0] == " "]
atoms = sum(len(list(r.get_atoms())) for r in std_res)
print(f"Chain {chain.id}: {len(std_res)} residues, {atoms} atoms")
关键参数
| 参数 | 端点 | 默认值 | 范围 / 选项 | 作用 |
|---|---|---|---|---|
value | 搜索 sequence | 必填 | 蛋白质/DNA/RNA 序列字符串 | MMseqs2 的查询序列 |
evalue_cutoff | 搜索 sequence | 0.1 | 1e-10–10 | E 值阈值 |
identity_cutoff | 搜索 sequence | 0.9 | 0.0–1.0 | 最低一致性比例 |
target | 搜索 sequence | "pdb_protein_sequence" | pdb_protein_sequence、pdb_dna_sequence、pdb_rna_sequence | 序列类型 |
operator | 搜索 text(属性) | 必填 | 参见属性运算符 | 比较类型 |
operator | 搜索 structure | strict_shape_match | strict_shape_match、relaxed_shape_match | 3D 匹配严格度 |
return_type | 所有搜索 | entry | entry、polymer_entity、assembly、polymer_instance、mol_definition、… | 标识符粒度 |
paginate.start / paginate.rows | request_options | 0 / 25 | 实际使用中每页最多约 10000 行 | 分页窗口 |
GraphQL 字段 entries(entry_ids: [...]) | data.rcsb.org/graphql | — | PDB ID 数组 | 批量获取条目元数据 |
最佳实践
-
先搜索,再获取:使用 Search API 获取 ID 列表,然后通过 GraphQL
entries(entry_ids: [...])批量获取元数据。避免每个 ID 单独发送一个 GraphQL 请求。 -
有意选择
full_text或text:自由文本关键词搜索需要"service": "full_text"。结构化属性筛选需要"service": "text"。两者不可互换。 -
优先使用 mmCIF,而非 PDB 格式:PDB 格式正在逐步淘汰,且原子数上限为 99,999。新代码始终应下载
.cif文件。 -
设置合理的
paginate.rows:批量处理时,rows: 100是不错的默认值;超过约 10000 行后,API 可能会变慢。使用paginate.start循环遍历全部结果。 -
在批量循环中用
time.sleep(0.2)限速:没有公布的硬性上限,但公共基础设施由多个用户共享。遇到HTTP 429时,应采用指数退避。 -
发送请求前检查负载:
print(json.dumps(payload, indent=2))是排查HTTP 400错误最简单的方法。 -
entries(entry_ids: [...])不会验证每个 ID:如果其中一个 ID 无效,整个数组都会返回null条目。如果无法信任 ID 的来源,请单独验证。
常用示例
示例:获取 PDB ID 对应的 FASTA
import requests
r = requests.get("https://www.rcsb.org/fasta/entry/4HHB", timeout=30)
print(r.text)
示例:获取条目中的所有链
import requests
DATA = "https://data.rcsb.org/graphql"
gql = """{ entry(entry_id: "4HHB") {
polymer_entities {
rcsb_id
rcsb_polymer_entity_container_identifiers { auth_asym_ids }
entity_poly { rcsb_entity_polymer_type pdbx_seq_one_letter_code_can }
}
} }"""
r = requests.post(DATA, json={"query": gql}, timeout=30)
for pe in r.json()["data"]["entry"]["polymer_entities"]:
chains = pe["rcsb_polymer_entity_container_identifiers"]["auth_asym_ids"]
seq = pe["entity_poly"]["pdbx_seq_one_letter_code_can"][:50]
print(f" {pe['rcsb_id']} chains={chains} type={pe['entity_poly']['rcsb_entity_polymer_type']} seq={seq}…")
示例:列出条目中的所有配体
import requests
DATA = "https://data.rcsb.org/graphql"
gql = """{ entry(entry_id: "1IEP") {
nonpolymer_entities {
rcsb_id
nonpolymer_comp { chem_comp { id name formula } }
}
} }"""
r = requests.post(DATA, json={"query": gql}, timeout=30)
for npe in r.json()["data"]["entry"]["nonpolymer_entities"]:
cc = npe["nonpolymer_comp"]["chem_comp"]
print(f" {npe['rcsb_id']}: {cc['id']} ({cc['name']}) {cc['formula']}")
示例:查看可用的搜索属性
Search API 在 https://search.rcsb.org/rcsbsearch/v2/metadata/schema 提供 JSON 架构。可通过该架构查找有效的属性路径。
import requests
r = requests.get("https://search.rcsb.org/rcsbsearch/v2/metadata/schema", timeout=30)
schema = r.json()
# Schema lists hundreds of attribute paths; sample a few
sample_paths = [k for k in schema if "resolution" in k.lower()][:5]
print(sample_paths)
故障排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
自由文本查询出现 HTTP 400 — Invalid request to the [ text ] service | 服务名称错误 | 关键词搜索使用 "service": "full_text";"service": "text" 用于结构化属性筛选 |
HTTP 400,并附带难以理解的架构错误消息 | 运算符/值的格式不正确 | 查看 AttributeQuery 运算符表;range 需要 {from,to,include_lower,include_upper} 字典 |
result_set 为空 | 筛选条件过严 | 每次放宽一个筛选条件;通过架构端点验证属性名称 |
对 entries(entry_ids: ["XYZW"]) 的请求出现 HTTP 404 | 条目不存在 | RCSB 会在 GraphQL 响应中返回 null,而不是 404 — 检查每个 data.entries[i] 是否为 null |
HTTP 429 Too Many Requests | 请求过于密集 | 在请求之间添加 time.sleep(0.3);遇到 429 时采用指数退避 |
搜索返回 HTTP 500 | 服务器端故障 | 5–10 s 后重试;查看 status.rcsb.org |
下载的 .pdb 文件被截断 | 原子数超过 99,999(旧格式限制) | 改为下载 .cif 文件 |
GraphQL 响应包含 errors 数组 | 字段名称拼写错误或根字段错误 | 阅读错误消息;API 对字段名称要求严格 — 查看 https://data.rcsb.org/index.html#graphql-api 中的架构浏览器 |
相关技能
- alphafold-database-access — AI 预测的结构;不存在实验结构时使用
- uniprot-protein-database — 蛋白质注释、序列、ID 映射(AlphaFold 需要 UniProt 登录号)
- biopython-molecular-biology — 解析下载的 PDB/mmCIF 文件、提取坐标、计算距离
- autodock-vina-docking — 使用 PDB 结构作为受体进行下游分子对接
- rdkit-cheminformatics — 分析从 PDB 复合物中提取的配体
参考资料
- RCSB PDB — 主门户和网页搜索
- Search API v2 文档 — 完整的 JSON 负载参考和 Swagger
- Data API GraphQL 浏览器 — 架构浏览器
- Search API 属性架构 — 所有可搜索属性的 JSON
- RCSB PDB Web API 概览 — 所有编程接口的索引
- 如需使用基于 SDK 的方式,请参阅 PyPI 包
rcsb-api;此 SKILL.md 直接使用底层 REST/GraphQL,因此无需安装 SDK。