mcpField
P
pdb-database

pdb-database

github.com/jaechang-hitsデータ・データベース38471 回インストール1 週間前に更新
リポジトリ

pdb-database について

このスキルガイドは、RCSB PDBの公開REST・GraphQL APIを使って生体高分子構造にアクセスする方法を扱います。構造生物学や創薬向けに、APIキーなしで検索、メタデータ取得、座標ファイルのダウンロードを行えます。

主な機能

  1. 01テキストや属性による構造検索
  2. 02配列・3D構造の類似性検索
  3. 03GraphQLによる構造メタデータ取得
  4. 04PDB、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 は、認証不要の3つの公開 REST エンドポイント(search.rcsb.org、data.rcsb.org、files.rcsb.org)を手軽に利用するためのものです。SDK が利用できない場合も、すべての操作は requests と小さな JSON ペイロードで再現できます。この SKILL.md では、requests がインストールされていればどの環境でもコードを実行できるよう、REST を一貫して使用します。

概要

RCSB PDB は、生体高分子の実験的に決定された構造を20万件以上収録する、3D 構造データの世界的なリポジトリです。プログラムからは、無料で認証不要の3つのエンドポイントを利用できます。

APIベース URLメソッド用途
検索https://search.rcsb.org/rcsbsearch/v2/queryPOST JSONテキスト、属性フィルター、配列、または3D類似性で PDB ID を検索
データhttps://data.rcsb.org/graphqlPOST GraphQL構造化メタデータ(エントリー、ポリマーエンティティ、アセンブリ、リガンド)を取得
ファイルhttps://files.rcsb.org/download/{id}.{format}GET座標ファイル(mmCIF、PDB、FASTA)をダウンロード

プログラムによる構造生物学の検索、創薬ターゲットの分析、タンパク質ファミリーの比較には、このスキルを使用してください。

使用する場面

  • キーワードや属性を指定して、タンパク質または核酸の結晶構造、クライオ電子顕微鏡構造、NMR 構造を検索する
  • クエリ配列(MMseqs2)または3D形状(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]}")

属性検索では、構造化された attribute/operator/value パラメーターとともに service: "text" を使用します。

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 記述子を使用して、3D形状が類似した構造を検索します。サービスは "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 の構造化メタデータを取得する標準的な方法です。1回のリクエストで、データ階層全体(entry → polymer_entity → assembly → 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 を使ってページネーションします(実際には1ページあたり最大約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
chemicalSMILES/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識別子の形式例
entry4HHBPDB ID ごとに1件
polymer_entity4HHB_1ポリマー鎖エンティティごとに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です
アセンブリ(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")

主要なパラメーター

パラメーターエンドポイントデフォルト範囲/選択肢効果
valuesearch sequence必須タンパク質/DNA/RNA の配列文字列MMseqs2 のクエリー配列
evalue_cutoffsearch sequence0.11e-10–10E-value のしきい値
identity_cutoffsearch sequence0.90.0–1.0同一性の最小割合
targetsearch sequence"pdb_protein_sequence"pdb_protein_sequence、pdb_dna_sequence、pdb_rna_sequence配列の種類
operatorsearch text(属性)必須属性演算子を参照比較の種類
operatorsearch 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. PDB 形式より mmCIF を使用する:PDB 形式は段階的に廃止されつつあり、原子数が 99,999 個に制限されています。新しいコードでは必ず .cif をダウンロードしてください。

  4. 現実的な paginate.rows を設定する:一括処理では rows: 100 が適切なデフォルトです。API は約10000行を超えると遅くなる可能性があります。すべての結果を取得するには、paginate.start を使ってループしてください。

  5. 一括処理のループでは time.sleep(0.2) でレートを制限する:公開されている厳密な上限はありませんが、パブリックインフラストラクチャは共有されています。HTTP 429 が返された場合は、指数バックオフを行ってください。

  6. 送信前にペイロードを確認する:print(json.dumps(payload, indent=2)) は HTTP 400 エラーのデバッグに最も手軽な方法です。

  7. entries(entry_ids: [...]) はすべての ID を検証しません:ID が1つでも間違っていると、配列全体でエントリーが 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演算子/値の形式が不正属性クエリー演算子の表を確認してください。range には {from,to,include_lower,include_upper} の辞書が必要です
result_set が空フィルターが厳しすぎるフィルターを1つずつ緩和し、スキーマエンドポイントで属性名を確認してください
entries(entry_ids: ["XYZW"]) で HTTP 404エントリーが存在しないRCSB は GraphQL のレスポンス内で 404 ではなく null を返します。各 data.entries[i] が null かどうか確認してください
HTTP 429 Too Many Requestsリクエスト頻度が高すぎるリクエスト間に time.sleep(0.3) を追加し、429 の場合は指数バックオフを行ってください
検索で HTTP 500サーバー側の一時的な問題5~10 秒後に再試行し、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 docs — JSON ペイロードの詳細なリファレンスと Swagger
  • Data API GraphQL browser — スキーマエクスプローラー
  • Search API attribute schema — 検索可能なすべての属性の JSON
  • RCSB PDB Web APIs overview — すべてのプログラム用インターフェースの一覧
  • SDK を使う場合は、rcsb-api PyPI パッケージを参照してください。この SKILL.md では基盤となる REST/GraphQL を直接使用するため、SDK のインストールは不要です。

コメント