drugbank-database-access について
drugbank-database-accessは、ローカルに保存したDrugBank XMLをPythonで検索するワークフローです。薬剤や相互作用の検索、標的・化学特性の抽出、外部データベースIDの対応付けに対応します。
主な機能
- 01ID・名称・CAS番号による薬剤検索
- 02薬剤相互作用の詳細取得
- 03標的・酵素・経路の抽出
- 04化学特性の取得と類似度計算
- 05外部データベースIDとの対応付け
活用シーン
- 01服薬リストの相互作用スクリーニング
- 02特定のタンパク質標的に関連する薬剤の検索
- 03ケモインフォマティクス用の薬剤特性比較
- 04DrugBankレコードと外部データベースIDの連結
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill drugbank-database-accessインストール後、Claude Code で/drugbank-database-accessを入力して読み込まれたか確認します。
同梱ファイルreferences/chemical_analysis.mdreferences/interactions_targets.md
詳細
DrugBank データベース — ローカル XML アクセス
概要
ローカルにダウンロードした XML ファイルを Python の ElementTree で解析し、DrugBank の包括的な医薬品データベース(14,000 件以上の医薬品エントリ、5,000 件以上のタンパク質ターゲット、17,000 件以上の医薬品相互作用)を検索します。医薬品検索、相互作用の確認、ターゲット/経路の抽出、化学的特性の分析、データベース間の識別子マッピングに対応しています。
使用する場面
- DrugBank ID、名前、または CAS 番号で医薬品情報(説明、適応症、作用機序、薬理学)を検索する場合
- 多剤併用の安全性を確認するため、医薬品間相互作用と重症度分類を調べる場合
- UniProt アクセッションを含む医薬品ターゲット、酵素、トランスポーター、キャリアを抽出する場合
- ケモインフォマティクス分析用に化学的特性(SMILES、InChI、分子量)を取得する場合
- DrugBank エントリを外部データベース(PubChem、ChEMBL、UniProt、KEGG)にマッピングする場合
- 分子フィンガープリントから医薬品類似性マトリックスを作成する場合
- ライブの生物活性データ(IC50、Ki、EC50)には、代わりに
chembl-database-bioactivityを使用してください - データベースをダウンロードせずに化合物の特性を検索するには、代わりに
pubchem-compound-searchを使用してください
前提条件
- DrugBank アカウント:https://go.drugbank.com/ で登録します(無料の学術ライセンス)
- XML のダウンロード:登録後、
drugbank_all_full_database.xml.zipをダウンロードします(展開後、約 1.5 GB) - Python パッケージ:
lxml、rdkit(類似性)、pandas(表形式の分析) - REST API(任意):開発者向けプランは月 3,000 リクエストです。一括処理にはローカル XML を使用してください
pip install lxml pandas
pip install rdkit-pypi # chemical similarity
pip install drugbank-downloader # programmatic XML download
クイックスタート
import xml.etree.ElementTree as ET
NS = {'db': 'http://www.drugbank.ca'} # Required for ALL XPath queries
tree = ET.parse('drugbank_all_full_database.xml') # 30-60s for full XML
root = tree.getroot()
# Build lookup index (DrugBank ID + lowercase name → element)
drug_index = {}
for drug in root.findall('db:drug', NS):
db_id = drug.find('db:drugbank-id[@primary="true"]', NS)
name = drug.find('db:name', NS)
if db_id is not None and name is not None:
drug_index[db_id.text] = drug
drug_index[name.text.lower()] = drug
def find_drug(query):
"""Find drug by DrugBank ID, name (case-insensitive), or CAS number."""
result = drug_index.get(query) or drug_index.get(query.lower())
if result is not None:
return result
for drug in root.findall('db:drug', NS): # CAS fallback
cas = drug.find('db:cas-number', NS)
if cas is not None and cas.text == query:
return drug
return None
drug = find_drug('DB00945') # Aspirin
name = drug.find('db:name', NS).text
print(f"{name}: {drug.find('db:description', NS).text[:100]}...")
コア API
1. データアクセスとセットアップ
import xml.etree.ElementTree as ET
NS = {'db': 'http://www.drugbank.ca'}
tree = ET.parse('drugbank_all_full_database.xml')
root = tree.getroot()
print(f"Total drug entries: {len(root.findall('db:drug', NS))}")
メモリに制約のある環境では、iterparse を使用してください。
drug_names = {}
for event, elem in ET.iterparse('drugbank_all_full_database.xml', events=('end',)):
if elem.tag == '{http://www.drugbank.ca}drug':
db_id = elem.find('{http://www.drugbank.ca}drugbank-id[@primary="true"]')
name = elem.find('{http://www.drugbank.ca}name')
if db_id is not None and name is not None:
drug_names[db_id.text] = name.text
elem.clear() # Free memory
print(f"Parsed {len(drug_names)} drugs via iterparse")
2. 医薬品情報の検索
def get_drug_info(drug_element):
"""Extract comprehensive drug information."""
def txt(path):
el = drug_element.find(path, NS)
return el.text if el is not None and el.text else None
return {
'drugbank_id': txt('db:drugbank-id[@primary="true"]'),
'name': txt('db:name'),
'type': drug_element.get('type'),
'description': txt('db:description'),
'indication': txt('db:indication'),
'mechanism_of_action': txt('db:mechanism-of-action'),
'cas_number': txt('db:cas-number'),
'groups': [g.text for g in drug_element.findall('db:groups/db:group', NS)],
}
info = get_drug_info(find_drug('Metformin'))
print(f"{info['name']} ({info['type']}): Groups={info['groups']}")
# Search by name pattern (partial match)
def search_by_name(pattern):
pattern_lower = pattern.lower()
return [d for d in root.findall('db:drug', NS)
if d.find('db:name', NS) is not None
and pattern_lower in d.find('db:name', NS).text.lower()]
statins = search_by_name('statin')
print(f"Found {len(statins)} drugs matching 'statin'")
3. 医薬品間相互作用
def get_interactions(drug_element):
"""Extract all drug-drug interactions."""
return [{
'drugbank_id': i.find('db:drugbank-id', NS).text,
'name': i.find('db:name', NS).text,
'description': i.find('db:description', NS).text,
} for i in drug_element.findall('db:drug-interactions/db:drug-interaction', NS)]
def classify_severity(description):
"""Classify severity from interaction description text."""
if not description:
return 'unknown'
dl = description.lower()
if any(w in dl for w in ['contraindicated', 'avoid', 'fatal', 'life-threatening']):
return 'major'
if any(w in dl for w in ['increase', 'decrease', 'enhance', 'reduce', 'alter']):
return 'moderate'
return 'minor'
interactions = get_interactions(find_drug('Aspirin'))
print(f"Aspirin has {len(interactions)} interactions")
for i in interactions[:3]:
print(f" [{classify_severity(i['description'])}] {i['name']}")
# Check pairwise interaction between two drugs
def check_interaction(drug1_elem, drug2_elem):
id2 = drug2_elem.find('db:drugbank-id[@primary="true"]', NS).text
for inter in get_interactions(drug1_elem):
if inter['drugbank_id'] == id2:
inter['severity'] = classify_severity(inter['description'])
return inter
return None
result = check_interaction(find_drug('Warfarin'), find_drug('Aspirin'))
if result:
print(f"[{result['severity']}] {result['description'][:150]}")
4. 医薬品ターゲットと経路
def get_targets(drug_element, target_type='targets'):
"""Extract targets/enzymes/transporters/carriers.
target_type: 'targets', 'enzymes', 'transporters', or 'carriers'
"""
results = []
for target in drug_element.findall(f'db:{target_type}/db:{target_type[:-1]}', NS):
t = {
'name': (target.find('db:name', NS).text
if target.find('db:name', NS) is not None else None),
'actions': [a.text for a in target.findall('db:actions/db:action', NS) if a.text],
}
poly = target.find('db:polypeptide', NS)
if poly is not None:
t['uniprot_id'] = poly.get('id')
gene = poly.find('db:gene-name', NS)
t['gene_name'] = gene.text if gene is not None else None
results.append(t)
return results
drug = find_drug('Imatinib')
targets = get_targets(drug, 'targets')
enzymes = get_targets(drug, 'enzymes')
print(f"Imatinib — Targets: {len(targets)}, Enzymes: {len(enzymes)}")
for t in targets[:3]:
print(f" {t['name']} (UniProt: {t.get('uniprot_id', 'N/A')}) — {t['actions']}")
def get_pathways(drug_element):
"""Extract SMPDB pathway associations."""
pathways = []
for pw in drug_element.findall('db:pathways/db:pathway', NS):
name = pw.find('db:name', NS)
smpdb = pw.find('db:smpdb-id', NS)
pathways.append({
'smpdb_id': smpdb.text if smpdb is not None else None,
'name': name.text if name is not None else None,
})
return pathways
for pw in get_pathways(find_drug('Metformin')):
print(f" {pw['smpdb_id']}: {pw['name']}")
5. 化学的特性と類似性
def get_property(drug_element, kind_name, section='calculated'):
"""Get a single property value by kind name."""
prefix = f'db:{section}-properties/db:property'
for prop in drug_element.findall(prefix, NS):
kind = prop.find('db:kind', NS)
if kind is not None and kind.text == kind_name:
return prop.find('db:value', NS).text
return None
def get_all_properties(drug_element):
"""Extract all calculated and experimental properties as a dict."""
props = {}
for section in ('calculated', 'experimental'):
for prop in drug_element.findall(f'db:{section}-properties/db:property', NS):
kind = prop.find('db:kind', NS)
value = prop.find('db:value', NS)
if kind is not None and value is not None:
key = f'{section}_{kind.text}' if section == 'experimental' else kind.text
props[key] = value.text
return props
drug = find_drug('Aspirin')
print(f"SMILES: {get_property(drug, 'SMILES')}")
print(f"MW: {get_property(drug, 'Molecular Weight')}")
print(f"LogP: {get_property(drug, 'logP')}")
# Tanimoto similarity between drugs using RDKit Morgan fingerprints
from rdkit import Chem
from rdkit.Chem import AllChem, DataStructs
def drug_similarity(drug1_elem, drug2_elem, radius=2, nbits=2048):
smi1, smi2 = get_property(drug1_elem, 'SMILES'), get_property(drug2_elem, 'SMILES')
if not smi1 or not smi2:
return None
mol1, mol2 = Chem.MolFromSmiles(smi1), Chem.MolFromSmiles(smi2)
if mol1 is None or mol2 is None:
return None
fp1 = AllChem.GetMorganFingerprintAsBitVect(mol1, radius, nBits=nbits)
fp2 = AllChem.GetMorganFingerprintAsBitVect(mol2, radius, nBits=nbits)
return DataStructs.TanimotoSimilarity(fp1, fp2)
sim = drug_similarity(find_drug('Aspirin'), find_drug('Ibuprofen'))
print(f"Aspirin vs Ibuprofen: {sim:.3f}")
6. データベース間の連携
def get_external_ids(drug_element):
"""Extract all external database identifiers."""
ids = {}
for ident in drug_element.findall('db:external-identifiers/db:external-identifier', NS):
resource = ident.find('db:resource', NS)
identifier = ident.find('db:identifier', NS)
if resource is not None and identifier is not None:
ids[resource.text] = identifier.text
return ids
ids = get_external_ids(find_drug('Imatinib'))
print(f"PubChem: {ids.get('PubChem Compound')}, ChEMBL: {ids.get('ChEMBL')}, "
f"KEGG: {ids.get('KEGG Drug')}, UniProt: {ids.get('UniProtKB')}")
# Build cross-reference table for multiple drugs
import pandas as pd
def build_crossref_table(names):
rows = []
for name in names:
d = find_drug(name)
if d is None: continue
ids = get_external_ids(d)
rows.append({'drug': name,
'drugbank_id': d.find('db:drugbank-id[@primary="true"]', NS).text,
'pubchem': ids.get('PubChem Compound'),
'chembl': ids.get('ChEMBL'),
'kegg': ids.get('KEGG Drug')})
return pd.DataFrame(rows)
print(build_crossref_table(['Aspirin', 'Metformin', 'Imatinib', 'Warfarin']).to_string(index=False))
主要な概念
XML 名前空間の処理
DrugBank XML のクエリにはすべて名前空間プレフィックスが必要です。これがないと、XPath は結果を返しません。
NS = {'db': 'http://www.drugbank.ca'}
name = drug.find('db:name', NS).text # CORRECT
name = drug.find('name') # WRONG — returns None!
# For iterparse, use full URI: '{http://www.drugbank.ca}drug'
医薬品エントリの構造
| セクション | XPath | 内容 |
|---|---|---|
| 識別情報 | db:drugbank-id、db:name、db:cas-number | 主な識別子 |
| 薬理学 | db:description、db:indication、db:mechanism-of-action | 臨床テキスト、作用機序、PD/PK |
| 相互作用 | db:drug-interactions/db:drug-interaction | 説明付きの相互作用する医薬品 |
| ターゲット | db:targets/db:target | 作用を含むタンパク質ターゲット |
| 酵素/トランスポーター/キャリア | db:enzymes/db:enzyme など | CYP450、P-gp、結合タンパク質 |
| 経路 | db:pathways/db:pathway | SMPDB 経路との関連 |
| 特性 | db:calculated-properties、db:experimental-properties | SMILES、MW、logP など |
| 外部 ID | db:external-identifiers | PubChem、ChEMBL、KEGG、UniProt との相互参照 |
外部識別子のマッピング
| XML 内のリソース名 | データベース | 例 |
|---|---|---|
PubChem Compound | PubChem CID | 2244 |
ChEMBL | ChEMBL | CHEMBL25 |
KEGG Drug / KEGG Compound | KEGG | D00109 / C01405 |
UniProtKB | UniProt | P23219 |
PharmGKB | PharmGKB | PA452615 |
ChEBI | ChEBI | 15365 |
計算された特性の種類
一般的な kind の値:SMILES、InChI、InChIKey、Molecular Weight、Molecular Formula、logP、logS、Polar Surface Area (PSA)、Rotatable Bond Count、H Bond Acceptor Count、H Bond Donor Count、pKa (strongest acidic)、pKa (strongest basic)、Rule of Five、Bioavailability。
一般的なワークフロー
ワークフロー 1:創薬ターゲットの分析
目標:特定の遺伝子を標的とするすべての医薬品を見つけ、その特性を分析します。
import xml.etree.ElementTree as ET
import pandas as pd
NS = {'db': 'http://www.drugbank.ca'}
tree = ET.parse('drugbank_all_full_database.xml')
root = tree.getroot()
target_gene = 'EGFR'
records = []
for drug in root.findall('db:drug', NS):
for target in drug.findall('db:targets/db:target', NS):
poly = target.find('db:polypeptide', NS)
if poly is None:
continue
gene = poly.find('db:gene-name', NS)
if gene is not None and gene.text == target_gene:
actions = [a.text for a in target.findall('db:actions/db:action', NS) if a.text]
records.append({
'drugbank_id': drug.find('db:drugbank-id[@primary="true"]', NS).text,
'name': drug.find('db:name', NS).text,
'groups': ', '.join(g.text for g in drug.findall('db:groups/db:group', NS)),
'actions': ', '.join(actions),
})
df = pd.DataFrame(records)
print(f"Drugs targeting {target_gene}: {len(df)}")
print(df.to_string(index=False))
ワークフロー 2:多剤併用の安全性スクリーニング
目標:服用薬のリストについて、すべてのペアごとの相互作用を重症度順にスクリーニングします。
import xml.etree.ElementTree as ET
import pandas as pd
NS = {'db': 'http://www.drugbank.ca'}
tree = ET.parse('drugbank_all_full_database.xml')
root = tree.getroot()
# Build index and interaction maps
idx = {}
inter_map = {} # drugbank_id → {interacting_id: description}
for drug in root.findall('db:drug', NS):
name = drug.find('db:name', NS)
db_id = drug.find('db:drugbank-id[@primary="true"]', NS)
if name is None or db_id is None:
continue
idx[name.text.lower()] = db_id.text
imap = {}
for i in drug.findall('db:drug-interactions/db:drug-interaction', NS):
imap[i.find('db:drugbank-id', NS).text] = i.find('db:description', NS).text
inter_map[db_id.text] = imap
medications = ['Warfarin', 'Aspirin', 'Omeprazole', 'Atorvastatin', 'Metformin']
report = []
med_ids = [(m, idx.get(m.lower())) for m in medications]
for i, (n1, id1) in enumerate(med_ids):
if not id1: continue
for n2, id2 in med_ids[i+1:]:
if not id2: continue
desc = inter_map.get(id1, {}).get(id2)
if desc:
dl = desc.lower()
sev = ('MAJOR' if any(w in dl for w in ['contraindicated','avoid','fatal'])
else 'MODERATE' if any(w in dl for w in ['increase','decrease','enhance','reduce'])
else 'MINOR')
report.append({'Drug 1': n1, 'Drug 2': n2, 'Severity': sev,
'Description': desc[:120]})
df = pd.DataFrame(report)
print(f"=== Polypharmacy Report: {len(medications)} medications, {len(df)} interactions ===")
if not df.empty:
print(df.sort_values('Severity').to_string(index=False))
主なパラメーター
| パラメーター | 関数/エンドポイント | デフォルト | 説明 |
|---|---|---|---|
NS(名前空間の辞書) | すべての XPath クエリ | {'db': 'http://www.drugbank.ca'} | すべての find/findall 呼び出しで必要です |
@primary="true" | db:drugbank-id | — | セカンダリ ID ではなく、プライマリ DrugBank ID(DB00XXX)を選択します |
target_type | get_targets() | 'targets' | 次のいずれかです:targets、enzymes、transporters、carriers |
radius | Morgan フィンガープリント | 2 | フィンガープリントの半径です。2 = ECFP4、3 = ECFP6 |
nbits | Morgan フィンガープリント | 2048 | ビットベクトルの長さです。値が大きいほどハッシュ衝突が少なくなります |
events | ET.iterparse() | — | 解析イベントです。終了タグで発火させるには ('end',) を使用します |
ベストプラクティス
- 起動時にメモリー内インデックスを構築する:一度だけ解析し(30~60秒)、ID と小文字の名前をキーにした辞書を構築します。ループ内で再解析しないでください
- 名前空間の辞書を必ず渡す:すべての
find()/findall()でNSが必要です。これを省略すると、結果が空になる原因の第1位になります - メモリーに制約がある場合は
iterparseを使用する:elem.clear()と組み合わせることで、1.5 GB のツリー全体を読み込まずに済みます - None をチェックする:すべての医薬品にすべてのフィールドがあるとは限りません。
.textの前に必ずel is not Noneを確認してください - 実験値より計算値を優先する:計算値(SMILES、logP、MW)は、ほぼすべての医薬品で利用できます
- 多剤併用では相互作用マップをキャッシュする:
{drug_id: {interacting_id: desc}}を一度だけ事前構築します
よくあるレシピ
レシピ:すべての医薬品プロパティを CSV にエクスポートする
import pandas as pd
records = []
for drug in root.findall('db:drug', NS):
row = {'drugbank_id': drug.find('db:drugbank-id[@primary="true"]', NS).text,
'name': drug.find('db:name', NS).text, 'type': drug.get('type')}
for prop in drug.findall('db:calculated-properties/db:property', NS):
row[prop.find('db:kind', NS).text] = prop.find('db:value', NS).text
records.append(row)
pd.DataFrame(records).to_csv('drugbank_properties.csv', index=False)
print(f"Exported {len(records)} drugs")
レシピ:Lipinski の Rule-of-5 フィルター
def check_lipinski(drug_element):
props = get_all_properties(drug_element)
try:
mw = float(props.get('Molecular Weight', 9999))
logp = float(props.get('logP', 99))
hba = int(props.get('H Bond Acceptor Count', 99))
hbd = int(props.get('H Bond Donor Count', 99))
except (ValueError, TypeError):
return None
violations = sum([mw > 500, logp > 5, hba > 10, hbd > 5])
return {'MW': mw, 'logP': logp, 'HBA': hba, 'HBD': hbd,
'violations': violations, 'passes': violations <= 1}
print(check_lipinski(find_drug('Imatinib')))
レシピ:CYP450 基質を検索する
cyp = 'CYP3A4'
substrates = []
for drug in root.findall('db:drug', NS):
for enz in drug.findall('db:enzymes/db:enzyme', NS):
n = enz.find('db:name', NS)
if n is not None and n.text and cyp.lower() in n.text.lower():
actions = [a.text.lower() for a in enz.findall('db:actions/db:action', NS) if a.text]
if 'substrate' in actions:
substrates.append(drug.find('db:name', NS).text)
print(f"{cyp} substrates: {len(substrates)}")
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
既知の要素に対して find() が None を返す | XML 名前空間が指定されていない | find()/findall() に必ず NS = {'db': 'http://www.drugbank.ca'} を渡してください |
XML 全体の解析時に MemoryError が発生する | メモリー使用量が約 2~3 GB | ET.iterparse() と elem.clear() を使用してください |
| 起動が遅い(60秒超) | 1.5 GB の XML を解析している | 一度だけ解析してインデックス辞書を構築し、再解析を避けてください |
| 名前で医薬品が見つからない | 大文字と小文字の区別、または別名 | 小文字に正規化し、CAS または DrugBank ID を試してください |
calculated-properties が空 | バイオテクノロジー医薬品/タンパク質医薬品には SMILES がない | drug.get('type') を確認してください。バイオテクノロジー医薬品には低分子のプロパティがありません |
AttributeError: 'NoneType' | 任意の XML 要素が存在しない | .text の前に el is not None を確認してください |
| 相互作用数が対称でない | XML 内の相互作用が対称ではない | 両方向を確認するか、対称なインデックスを構築してください |
drugbank-downloader の認証失敗 | 認証情報が無効 | https://go.drugbank.com/ でアカウントを確認してください |
REST API の 429 | レート制限を超過した | バッチクエリではローカル XML に切り替えてください |
バンドルされたリソース
references/interactions_targets.md — 相互作用(重大度のヒューリスティクス、一括スクリーニング、説明の解析)とターゲット/経路(ポリペプチドの詳細、アクションカタログ、酵素/トランスポーターの対象範囲、経路エンリッチメント)をまとめています。基本的な抽出(Core API 3-4)はインラインに移動しました。Core API と重複するフィールドごとの詳細な解析は省略しました。
references/chemical_analysis.md — 特性の抽出、記述子の計算、フィンガープリントの類似性、薬物らしさによるフィルタリングを扱います。対象:特性の完全なカタログ、類似性マトリックス、部分構造検索。SMILES/InChI の抽出と Tanimoto(Core API 5)、Lipinski(レシピ)はインラインに移動しました。3D コンフォーマーは省略しました(rdkit-cheminformatics を使用してください)。
元の構成(2,717 行:SKILL.md 190 行 + 参照ファイル 5 件 2,166 行 + スクリプト 351 行):
SKILL.md(190) — スタブを 6 つの Core API モジュールで書き換えdata-access.md(243)→ Core API 1 + クイックスタートdrug-queries.md(387)→ Core API 2interactions.md(426)→references/interactions_targets.md+ Core API 3targets-pathways.md(519)→references/interactions_targets.md+ Core API 4chemical-analysis.md(591)→references/chemical_analysis.md+ Core API 5drugbank_helper.py(351)— 薄いラッパー:find_drug→ クイックスタート、get_drug_info/search_by_name→ Core API 2、get_interactions/check_interaction/check_polypharmacy→ Core API 3、get_targets→ Core API 4、get_properties/get_smiles/get_inchi→ Core API 5
保持率:SKILL.md は約 550 行です。参照ファイル(約 600 行)を含めると、合計で約 1,150 / 2,717 = 約 42% です。元のスタブはそのままです。参照ファイル 5 件を 2 件に統合(ceil(5/3)=2)。
関連スキル
- chembl-database-bioactivity — ライブの生物活性データベース(IC50、Ki、EC50)。DrugBank の静的な医薬品カタログを補完します
- pubchem-compound-search — データベースをダウンロードせずに公開化合物の特性を検索
- rdkit-cheminformatics — 3D コンフォーマー、高度なフィンガープリント、DrugBank の特性を超える記述子に対応する、包括的なケモインフォマティクスツールキット
参考資料
- DrugBank のウェブサイト:https://go.drugbank.com/
- DrugBank XML スキーマ:https://docs.drugbank.com/xml/
- drugbank-downloader:https://pypi.org/project/drugbank-downloader/
- Wishart DS et al.(2018)。DrugBank 5.0。Nucleic Acids Res. 46(D1):D1074-D1082。https://doi.org/10.1093/nar/gkx1037