pdb-database について
このスキルガイドは、RCSB PDBの公開REST・GraphQL APIを使って生体高分子構造にアクセスする方法を扱います。構造生物学や創薬向けに、APIキーなしで検索、メタデータ取得、座標ファイルのダウンロードを行えます。
主な機能
- 01テキストや属性による構造検索
- 02配列・3D構造の類似性検索
- 03GraphQLによる構造メタデータ取得
- 04PDB、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 は、認証不要の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/query | POST JSON | テキスト、属性フィルター、配列、または3D類似性で PDB ID を検索 |
| データ | https://data.rcsb.org/graphql | POST 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 |
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 ごとに1件 |
polymer_entity | 4HHB_1 | ポリマー鎖エンティティごとに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です |
| アセンブリ(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 | search sequence | 必須 | タンパク質/DNA/RNA の配列文字列 | MMseqs2 のクエリー配列 |
evalue_cutoff | search sequence | 0.1 | 1e-10–10 | E-value のしきい値 |
identity_cutoff | search sequence | 0.9 | 0.0–1.0 | 同一性の最小割合 |
target | search sequence | "pdb_protein_sequence" | pdb_protein_sequence、pdb_dna_sequence、pdb_rna_sequence | 配列の種類 |
operator | search text(属性) | 必須 | 属性演算子を参照 | 比較の種類 |
operator | search 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"を使います。これらは相互に置き換えられません。 -
PDB 形式より mmCIF を使用する:PDB 形式は段階的に廃止されつつあり、原子数が 99,999 個に制限されています。新しいコードでは必ず
.cifをダウンロードしてください。 -
現実的な
paginate.rowsを設定する:一括処理ではrows: 100が適切なデフォルトです。API は約10000行を超えると遅くなる可能性があります。すべての結果を取得するには、paginate.startを使ってループしてください。 -
一括処理のループでは
time.sleep(0.2)でレートを制限する:公開されている厳密な上限はありませんが、パブリックインフラストラクチャは共有されています。HTTP 429が返された場合は、指数バックオフを行ってください。 -
送信前にペイロードを確認する:
print(json.dumps(payload, indent=2))はHTTP 400エラーのデバッグに最も手軽な方法です。 -
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-apiPyPI パッケージを参照してください。この SKILL.md では基盤となる REST/GraphQL を直接使用するため、SDK のインストールは不要です。