mcpField
P
pdb-database

pdb-database

github.com/jaechang-hits数据与数据库38471 次安装1 周前更新
代码仓库

关于 pdb-database

这份技能指南通过 RCSB PDB 的公开 REST 和 GraphQL 接口访问生物大分子结构数据,面向结构生物学与药物发现工作。它涵盖结构搜索、元数据获取和坐标文件下载,无需 API 密钥。

主要功能

  1. 01按文本和属性搜索结构
  2. 02进行序列与三维结构相似性搜索
  3. 03通过 GraphQL 获取结构元数据
  4. 04下载 PDB、mmCIF 和 FASTA 文件
  5. 05组合查询并批量分页获取结果

适用场景

  1. 01寻找带特定属性的蛋白质结构
  2. 02比较同源蛋白的结构记录
  3. 03收集药物靶点结构及配体信息
  4. 04下载结构文件用于对接或分子动力学

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill pdb-database

安装后在 Claude Code 中输入/pdb-database确认已加载。

详细介绍

PDB 数据库

为什么没有 SDK? rcsb-api Python SDK 只是对三个公开、无需身份验证的 REST 端点(search.rcsb.org、data.rcsb.org、files.rcsb.org)的便捷封装。SDK 不可用时,每项操作都可以通过普通的 requests 和一个小型 JSON 负载来复现。本 SKILL.md 全程使用 REST 方式,因此只要环境中安装了 requests,代码就能运行。

概述

RCSB PDB 是全球生物大分子三维结构数据的存储库,收录了 200,000 多个通过实验测定的结构。可通过三个免费、无需身份验证的端点进行编程访问:

API基础 URL方法用途
Searchhttps://search.rcsb.org/rcsbsearch/v2/queryPOST JSON通过文本、属性筛选条件、序列或三维相似性查找 PDB ID
Datahttps://data.rcsb.org/graphqlPOST GraphQL获取结构化元数据(条目、聚合物实体、装配体、配体)
Fileshttps://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
sequenceMMseqs2 序列相似性搜索target ∈ {pdb_protein_sequence、pdb_dna_sequence、pdb_rna_sequence},value(序列)、evalue_cutoff、identity_cutoff
seqmotif模式 / 正则表达式 / PROSITE 模体value(模式)、pattern_type ∈ {simple、prosite、regex}
structure3D 形状相似性(BioZernike)value({entry_id, assembly_id})、operator ∈ {strict_shape_match、relaxed_shape_match}
strucmotif3D 残基排列模体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标识符形式示例
entry4HHB每个 PDB ID 一个
polymer_entity4HHB_1每个聚合物链实体一个
non_polymer_entity4HHB_2配体、辅因子
assembly4HHB-1生物学单元
polymer_instance4HHB.A单条链的坐标
mol_definitionHEM化学组分(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 模式备注
mmCIFhttps://files.rcsb.org/download/{id}.cif推荐;没有原子数量限制
PDBhttps://files.rcsb.org/download/{id}.pdb旧格式;原子数量上限为 99,999
Assembly(mmCIF)https://files.rcsb.org/download/{id}-assembly{N}.cif生物学单元
FASTAhttps://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搜索 sequence0.11e-10–10E 值阈值
identity_cutoff搜索 sequence0.90.0–1.0最低一致性比例
target搜索 sequence"pdb_protein_sequence"pdb_protein_sequence、pdb_dna_sequence、pdb_rna_sequence序列类型
operator搜索 text(属性)必填参见属性运算符比较类型
operator搜索 structurestrict_shape_matchstrict_shape_match、relaxed_shape_match3D 匹配严格度
return_type所有搜索entryentry、polymer_entity、assembly、polymer_instance、mol_definition、…标识符粒度
paginate.start / paginate.rowsrequest_options0 / 25实际使用中每页最多约 10000 行分页窗口
GraphQL 字段 entries(entry_ids: [...])data.rcsb.org/graphql—PDB ID 数组批量获取条目元数据

最佳实践

  1. 先搜索,再获取:使用 Search API 获取 ID 列表,然后通过 GraphQL entries(entry_ids: [...]) 批量获取元数据。避免每个 ID 单独发送一个 GraphQL 请求。

  2. 有意选择 full_text 或 text:自由文本关键词搜索需要 "service": "full_text"。结构化属性筛选需要 "service": "text"。两者不可互换。

  3. 优先使用 mmCIF,而非 PDB 格式:PDB 格式正在逐步淘汰,且原子数上限为 99,999。新代码始终应下载 .cif 文件。

  4. 设置合理的 paginate.rows:批量处理时,rows: 100 是不错的默认值;超过约 10000 行后,API 可能会变慢。使用 paginate.start 循环遍历全部结果。

  5. 在批量循环中用 time.sleep(0.2) 限速:没有公布的硬性上限,但公共基础设施由多个用户共享。遇到 HTTP 429 时,应采用指数退避。

  6. 发送请求前检查负载:print(json.dumps(payload, indent=2)) 是排查 HTTP 400 错误最简单的方法。

  7. 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 复合物中提取的配体

参考资料

评论

支持 Markdown