pubmed-database について
NCBI E-utilities REST API を使って PubMed を検索し、文献レコードを取得する方法を生物医学研究者向けに解説します。ブール検索や MeSH クエリ、バッチ取得、引用照合、自動化ワークフローを扱います。
主な機能
- 01ブール検索と MeSH クエリの作成
- 02フィールド・出版タイプによる絞り込み
- 03PMID の検索とレコード取得
- 04関連論文の検索と引用照合
- 05History Server によるバッチ取得
- 06日付フィルターによる新着論文の追跡
活用シーン
- 01システマティックレビューやメタ分析の検索
- 02テーマ別の新着論文の監視
- 03PMID または DOI による抄録・メタデータ取得
- 04PubMed レコードの一括処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill pubmed-databaseインストール後、Claude Code で/pubmed-databaseを入力して読み込まれたか確認します。
同梱ファイルreferences/common_queries.mdreferences/search_syntax.md
詳細
PubMed データベース
概要
PubMed は、米国国立医学図書館が提供するデータベースで、MEDLINE および生命科学系ジャーナルの 3,600 万件以上の生物医学文献の引用情報に無料でアクセスできます。このスキルでは、E-utilities REST API によるプログラムからのアクセスと、ブール演算子、MeSH 用語、フィールドタグを使った高度な検索クエリの構築について説明します。
使用する場面
- 構造化されたブール演算子/MeSH クエリを使って生物医学文献を検索する
- 文献の自動モニタリングや抽出のパイプラインを構築する
- システマティックレビューやメタアナリシスを実施する
- PMID/DOI で論文のメタデータ、抄録、引用情報を取得する
- 関連論文を見つけたり、引用ネットワークをプログラムから調査したりする
- PubMed レコードの大規模なセットを一括処理する
- Python ネイティブの PubMed アクセスには BioPython(
Bio.Entrez)を優先してください。このスキルでは REST API の直接利用を扱います - より幅広い学術検索(生物医学以外)には、OpenAlex または Semantic Scholar の API を使用してください
前提条件
pip install requests # HTTP client for E-utilities API
# Optional: pip install biopython — Bio.Entrez wrapper (higher-level API)
API のレート制限:
- API キーなし:毎秒 3 リクエスト
- API キーあり:毎秒 10 リクエスト(https://www.ncbi.nlm.nih.gov/account/ で登録してください)
- 連絡先メールアドレスを含む
User-Agentヘッダーを必ず指定してください
クイックスタート
import requests
import time
BASE_URL = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/"
API_KEY = "YOUR_API_KEY" # Optional but recommended
def pubmed_request(endpoint, params):
"""Reusable helper for E-utilities API calls with rate limiting."""
params.setdefault("api_key", API_KEY)
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
response.raise_for_status()
time.sleep(0.1 if API_KEY != "YOUR_API_KEY" else 0.34) # Rate limit
return response
# Search → Fetch workflow
search_resp = pubmed_request("esearch.fcgi", {
"db": "pubmed", "term": "CRISPR[tiab] AND 2024[dp]",
"retmax": 5, "retmode": "json"
})
pmids = search_resp.json()["esearchresult"]["idlist"]
print(f"Found {len(pmids)} articles: {pmids}")
fetch_resp = pubmed_request("efetch.fcgi", {
"db": "pubmed", "id": ",".join(pmids),
"rettype": "abstract", "retmode": "text"
})
print(fetch_resp.text[:500])
コア API
1. 検索クエリの構築
ブール演算子、フィールドタグ、MeSH 用語を使って PubMed クエリを作成します。
# Boolean operators: AND, OR, NOT (must be uppercase)
queries = {
"basic": "diabetes AND treatment AND 2024[dp]",
"synonyms": "(metformin OR insulin) AND type 2 diabetes",
"exclude": "cancer NOT review[pt]",
"phrase": '"gene expression" AND RNA-seq',
"field_tags": "smith ja[au] AND cancer[tiab] AND 2023[dp]",
}
# Common field tags:
# [tiab] = title/abstract [au] = author [mh] = MeSH term
# [pt] = publication type [dp] = date [ta] = journal
# [1au] = first author [lastau] = last author
# [affil] = affiliation [doi] = DOI [pmid] = PubMed ID
# Date filtering
date_queries = {
"single_year": "cancer AND 2024[dp]",
"range": "cancer AND 2020:2024[dp]",
"specific": "cancer AND 2024/03/15[dp]",
}
# MeSH terms — controlled vocabulary for precise searching
mesh_queries = {
# [mh] includes narrower terms automatically
"broad": "diabetes mellitus[mh]",
# [majr] limits to major topic focus
"focused": "diabetes mellitus[majr]",
# MeSH + subheading
"therapy": "diabetes mellitus, type 2[mh]/drug therapy",
# Substance name
"drug": "metformin[nm] AND diabetes mellitus[mh]",
}
# Common MeSH subheadings:
# /diagnosis /drug therapy /epidemiology /etiology
# /prevention & control /therapy /genetics
2. ESearch — PMID の検索と取得
# Basic search
resp = pubmed_request("esearch.fcgi", {
"db": "pubmed",
"term": "CRISPR[tiab] AND genome editing[tiab] AND 2024[dp]",
"retmax": 100,
"retmode": "json",
"sort": "relevance", # or "pub_date", "first_author"
})
result = resp.json()["esearchresult"]
pmids = result["idlist"]
total = result["count"]
print(f"Total hits: {total}, Retrieved: {len(pmids)}")
# With history server (for large result sets > 500)
resp = pubmed_request("esearch.fcgi", {
"db": "pubmed",
"term": "cancer AND 2024[dp]",
"usehistory": "y",
"retmode": "json",
})
result = resp.json()["esearchresult"]
webenv = result["webenv"]
query_key = result["querykey"]
total = int(result["count"])
print(f"Stored {total} results on history server")
3. EFetch — 全レコードのダウンロード
# Fetch abstracts as text
resp = pubmed_request("efetch.fcgi", {
"db": "pubmed",
"id": ",".join(pmids[:10]),
"rettype": "abstract",
"retmode": "text",
})
print(resp.text[:500])
# Fetch XML for structured parsing
resp = pubmed_request("efetch.fcgi", {
"db": "pubmed",
"id": ",".join(pmids[:10]),
"rettype": "xml",
"retmode": "xml",
})
# Fetch from history server (batch processing)
batch_size = 500
for start in range(0, total, batch_size):
resp = pubmed_request("efetch.fcgi", {
"db": "pubmed",
"query_key": query_key,
"WebEnv": webenv,
"retstart": start,
"retmax": batch_size,
"rettype": "xml",
"retmode": "xml",
})
print(f"Fetched records {start}–{start + batch_size}")
time.sleep(0.5) # Extra delay for large batches
4. ESummary と ELink — 要約と関連論文
# ESummary — lightweight document summaries
resp = pubmed_request("esummary.fcgi", {
"db": "pubmed",
"id": ",".join(pmids[:5]),
"retmode": "json",
})
for uid, data in resp.json()["result"].items():
if uid == "uids":
continue
print(f"PMID {uid}: {data.get('title', '')[:80]}")
print(f" Journal: {data.get('fulljournalname', '')}, "
f"Date: {data.get('pubdate', '')}")
# ELink — find related articles
resp = pubmed_request("elink.fcgi", {
"dbfrom": "pubmed",
"db": "pubmed",
"id": pmids[0],
"cmd": "neighbor",
"retmode": "json",
})
# Links to other NCBI databases
resp = pubmed_request("elink.fcgi", {
"dbfrom": "pubmed",
"db": "pmc", # PubMed Central
"id": pmids[0],
"retmode": "json",
})
5. 引用情報の照合と識別子の検索
# Search by identifiers
id_queries = {
"pmid": "12345678[pmid]",
"doi": "10.1056/NEJMoa123456[doi]",
"pmc": "PMC123456[pmc]",
}
# ECitMatch — match partial citations to PMIDs
# Format: journal|year|volume|first_page|author_name|key|
citation = "Science|2008|320|5880|1185|key1|"
resp = pubmed_request("ecitmatch.cgi", {
"db": "pubmed",
"rettype": "xml",
"bdata": citation,
})
print(f"Matched PMID: {resp.text.strip()}")
# Batch citation matching
citations = [
"Nature|2020|580|7801|71|ref1|",
"Science|2019|366|6463|347|ref2|",
]
resp = pubmed_request("ecitmatch.cgi", {
"db": "pubmed",
"rettype": "xml",
"bdata": "\r".join(citations),
})
6. 出版物の絞り込み
# Filter by publication type
type_filters = {
"rcts": "randomized controlled trial[pt]",
"reviews": "systematic review[pt]",
"meta": "meta-analysis[pt]",
"guidelines": "guideline[pt]",
"case_reports": "case reports[pt]",
}
# Filter by text availability
availability = {
"free_text": "free full text[sb]",
"has_abstract": "hasabstract[text]",
}
# Combine filters
query = (
"diabetes mellitus[mh] AND "
"randomized controlled trial[pt] AND "
"2023:2024[dp] AND "
"free full text[sb] AND "
"english[la]"
)
resp = pubmed_request("esearch.fcgi", {
"db": "pubmed", "term": query, "retmax": 100, "retmode": "json"
})
print(f"Free RCTs on diabetes (2023-2024): {resp.json()['esearchresult']['count']}")
主な概念
E-utilities エンドポイントの概要
| エンドポイント | 目的 | 主なパラメーター |
|---|---|---|
esearch.fcgi | 検索して PMID を返す | term、retmax、sort、usehistory |
efetch.fcgi | 全レコードをダウンロードする | id/query_key+WebEnv、rettype、retmode |
esummary.fcgi | 軽量な要約 | id、retmode |
epost.fcgi | UID をサーバーにアップロードする | id(カンマ区切り) |
elink.fcgi | 関連論文、データベース間のリンク | id、dbfrom、db、cmd |
einfo.fcgi | データベース/フィールドの一覧 | db(任意) |
egquery.fcgi | データベース全体でヒット数を数える | term |
espell.fcgi | スペル候補 | term |
ecitmatch.cgi | 引用情報を PMID と照合する | bdata |
History サーバーパターン
500 件を超える検索結果セットでは、URL の長さ制限を避けるために History サーバーを使用します。
usehistory=yを指定して ESearch を実行 →WebEnv+QueryKeyを返しますWebEnv+QueryKey+retstart/retmaxを使用して、EFetch でバッチ取得します- EPost を使用して、追加の PMID を同じ
WebEnvにアップロードします
自動用語マッピング(ATM)
フィールドタグが指定されていない場合、PubMed は用語を次の順にマッピングします:MeSH Translation Table → Journals Translation Table → Author Index → Full Text。明示的なフィールドタグまたは引用符で囲んだフレーズを使うと、ATM を回避できます。
よく使われる MeSH サブヘディング
| サブヘディング | 略語 | 用途 |
|---|---|---|
| /diagnosis | /DI | 診断法 |
| /drug therapy | /DT | 薬物療法 |
| /epidemiology | /EP | 疾患の発生パターン |
| /etiology | /ET | 疾患の原因 |
| /genetics | /GE | 遺伝学的側面 |
| /prevention & control | /PC | 予防措置 |
| /therapy | /TH | 治療法 |
一般的なワークフロー
ワークフロー 1:システマティックレビューの検索
import requests, time, json
# 1. Define PICO-structured query
query = (
# Population
"(diabetes mellitus, type 2[mh] OR type 2 diabetes[tiab]) AND "
# Intervention + Comparison
"(metformin[nm] OR lifestyle modification[tiab]) AND "
# Outcome
"(glycemic control[tiab] OR HbA1c[tiab]) AND "
# Study design filter
"(randomized controlled trial[pt] OR systematic review[pt]) AND "
# Date range
"2020:2024[dp]"
)
# 2. Search with history server
resp = pubmed_request("esearch.fcgi", {
"db": "pubmed", "term": query,
"usehistory": "y", "retmode": "json"
})
result = resp.json()["esearchresult"]
total = int(result["count"])
print(f"Systematic review hits: {total}")
# 3. Batch fetch all results as XML
import xml.etree.ElementTree as ET
articles = []
for start in range(0, total, 200):
resp = pubmed_request("efetch.fcgi", {
"db": "pubmed", "query_key": result["querykey"],
"WebEnv": result["webenv"],
"retstart": start, "retmax": 200,
"rettype": "xml", "retmode": "xml"
})
root = ET.fromstring(resp.text)
for article in root.findall('.//PubmedArticle'):
pmid = article.findtext('.//PMID')
title = article.findtext('.//ArticleTitle')
articles.append({"pmid": pmid, "title": title})
time.sleep(0.5)
print(f"Retrieved {len(articles)} articles for screening")
ワークフロー 2:文献モニタリングのパイプライン
import json, datetime
# 1. Construct monitoring query
topic_query = (
"(CRISPR[tiab] OR gene editing[tiab]) AND "
"(therapeutics[tiab] OR clinical trial[pt])"
)
# 2. Search recent publications (last 30 days)
today = datetime.date.today()
start_date = today - datetime.timedelta(days=30)
query = f"{topic_query} AND {start_date.strftime('%Y/%m/%d')}:{today.strftime('%Y/%m/%d')}[dp]"
resp = pubmed_request("esearch.fcgi", {
"db": "pubmed", "term": query,
"retmax": 100, "retmode": "json", "sort": "pub_date"
})
pmids = resp.json()["esearchresult"]["idlist"]
# 3. Get summaries for new articles
if pmids:
resp = pubmed_request("esummary.fcgi", {
"db": "pubmed", "id": ",".join(pmids), "retmode": "json"
})
for uid in pmids:
info = resp.json()["result"].get(uid, {})
print(f"[{uid}] {info.get('title', 'N/A')[:80]}")
print(f" {info.get('fulljournalname', '')} — {info.get('pubdate', '')}")
主要なパラメーター
| パラメーター | エンドポイント | デフォルト | 効果 |
|---|---|---|---|
term | ESearch | 必須 | Boolean/フィールドタグを使った検索クエリ |
retmax | ESearch/EFetch | 20 | 返されるレコードの最大数(最大10,000件) |
retstart | ESearch/EFetch | 0 | ページネーションのオフセット |
rettype | EFetch | full | 出力タイプ:abstract、medline、xml、uilist |
retmode | すべて | xml | 出力形式:xml、json、text |
sort | ESearch | relevance | ソート順:relevance、pub_date、first_author |
usehistory | ESearch | n | 履歴サーバーを有効化します。大量の検索結果には y を指定します |
api_key | すべて | なし | 10リクエスト/秒(なしの場合は3リクエスト/秒)で利用するためのNCBI APIキー |
cmd | ELink | neighbor | リンクタイプ:neighbor、neighbor_score、prlinks |
datetype | ESearch | pdat | 日付フィールド:pdat(出版日)、edat(Entrez) |
ベストプラクティス
- 必ずAPIキーを使用します — NCBIに登録すると、毎秒3件ではなく10件のリクエストを送信できます
- 500件を超える検索結果には履歴サーバーを使用します — URLの長さ制限を回避でき、バッチ取得が可能になります
- レート制限を設けます — APIキーを使用する場合は
time.sleep(0.1)、使用しない場合はtime.sleep(0.34)を指定します - 結果をローカルにキャッシュします — PubMedのデータは頻繁には変わらないため、応答をキャッシュしてAPI呼び出しを最小限に抑えます
- MeSH用語とフリーテキストを併用します — 網羅的に検索するには
[mh]と[tiab]を組み合わせます:(diabetes mellitus[mh] OR diabetes[tiab]) - 検索方法を記録します — システマティックレビューでは、正確なクエリ、日付、検索結果数を記録します
- 構造化データにはXMLを解析します — テキスト出力は人が読みやすい形式ですが、XMLでは自動抽出に必要なフィールド構造が保持されます
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| HTTP 429(リクエストが多すぎます) | レート制限超過 | time.sleep() を追加します。上限を引き上げるにはAPIキーを使用します |
| HTTP 414(URIが長すぎます) | URLにPMIDが多すぎます | 履歴サーバー(usehistory=y)またはEPostを使用します |
| 検索結果が空 | クエリの条件が厳しすぎます | フィルターを1つずつ削除します。EInfoでATMを確認します |
| 想定外のMeSHマッピング | 自動用語マッピング | term 単独ではなく、term[tiab] のように明示的なフィールドタグを使用します |
| 抄録が見つからない | 1975年以前の記事、または特定の種類の記事 | hasabstract[text] でフィルタリングします |
| XML解析エラー | 応答の形式が不正です | retmode=xml と rettype=xml を確認し、エンコーディングを処理します |
| 履歴サーバーの情報が古い | セッションの有効期限切れ(8時間の非アクティブ状態) | usehistory=y を指定してESearchを再実行し、新しいWebEnvを取得します |
| 結果が途中で切れる | デフォルトの retmax=20 | retmax=100 以上を指定します(最大10,000件) |
よく使うレシピ
レシピ:遺伝子セットの抄録をダウンロードする
import requests
import time
def fetch_abstracts(gene_list, max_per_gene=5):
"""Retrieve PubMed abstracts for each gene in a list."""
base = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
records = []
for gene in gene_list:
r = requests.get(f"{base}/esearch.fcgi",
params={"db": "pubmed", "term": f"{gene}[gene] AND Homo sapiens[orgn]",
"retmax": max_per_gene, "retmode": "json"})
ids = r.json()["esearchresult"]["idlist"]
if ids:
fetch = requests.get(f"{base}/efetch.fcgi",
params={"db": "pubmed", "id": ",".join(ids), "rettype": "abstract"})
records.append({"gene": gene, "pmids": ids, "text": fetch.text[:500]})
time.sleep(0.34)
return records
results = fetch_abstracts(["BRCA1", "TP53", "EGFR"])
for r in results:
print(f"{r['gene']}: {r['pmids']}")
レシピ:日付フィルターを使って新しい出版物を追跡する
import requests
from datetime import date, timedelta
# Find papers published in the last 7 days on a topic
week_ago = (date.today() - timedelta(days=7)).strftime("%Y/%m/%d")
today = date.today().strftime("%Y/%m/%d")
resp = requests.get("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi",
params={"db": "pubmed", "term": "CRISPR AND cancer",
"datetype": "pdat", "mindate": week_ago, "maxdate": today,
"retmax": 20, "retmode": "json"})
data = resp.json()["esearchresult"]
print(f"New CRISPR+cancer papers this week: {data['count']}")
print("PMIDs:", data["idlist"])
同梱リソース
references/search_syntax.md— フィールドタグ、Boolean/ワイルドカード/近接検索の構文、自動用語マッピングのルール、すべてのフィルタータイプ(年齢層、種、言語)、臨床クエリフィルターを網羅したリファレンスreferences/common_queries.md— 分野別(疾患、対象集団、方法論、医薬品研究、疫学)に整理された、すぐに使えるクエリテンプレートと約40の例
元のファイルから移行されていないもの:references/api_reference.md(298行)— エンドポイントのパラメーターの詳細は、「Core API」セクション2~5および「Key Concepts」の「E-utilities Endpoint Summary」テーブルにまとめられています。
参考資料
- PubMedヘルプ:https://pubmed.ncbi.nlm.nih.gov/help/
- E-utilitiesドキュメント:https://www.ncbi.nlm.nih.gov/books/NBK25501/
- NCBI APIキーの登録:https://www.ncbi.nlm.nih.gov/account/
関連スキル
- biopython — E-utilities用の高水準Pythonラッパー(
Bio.Entrez) - openalex-database — 生物医学以外も対象とする、より広範な学術文献
- literature-review — システマティックレビューの方法論とPRISMAフレームワーク