mcpField
P
pubmed-database

pubmed-database

github.com/jaechang-hits生産性38476 回インストール1 週間前に更新
リポジトリ

pubmed-database について

NCBI E-utilities REST API を使って PubMed を検索し、文献レコードを取得する方法を生物医学研究者向けに解説します。ブール検索や MeSH クエリ、バッチ取得、引用照合、自動化ワークフローを扱います。

主な機能

  1. 01ブール検索と MeSH クエリの作成
  2. 02フィールド・出版タイプによる絞り込み
  3. 03PMID の検索とレコード取得
  4. 04関連論文の検索と引用照合
  5. 05History Server によるバッチ取得
  6. 06日付フィルターによる新着論文の追跡

活用シーン

  1. 01システマティックレビューやメタ分析の検索
  2. 02テーマ別の新着論文の監視
  3. 03PMID または DOI による抄録・メタデータ取得
  4. 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.fcgiUID をサーバーにアップロードするid(カンマ区切り)
elink.fcgi関連論文、データベース間のリンクid、dbfrom、db、cmd
einfo.fcgiデータベース/フィールドの一覧db(任意)
egquery.fcgiデータベース全体でヒット数を数えるterm
espell.fcgiスペル候補term
ecitmatch.cgi引用情報を PMID と照合するbdata
History サーバーパターン

500 件を超える検索結果セットでは、URL の長さ制限を避けるために History サーバーを使用します。

  1. usehistory=y を指定して ESearch を実行 → WebEnv + QueryKey を返します
  2. WebEnv + QueryKey + retstart/retmax を使用して、EFetch でバッチ取得します
  3. 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', '')}")

主要なパラメーター

パラメーターエンドポイントデフォルト効果
termESearch必須Boolean/フィールドタグを使った検索クエリ
retmaxESearch/EFetch20返されるレコードの最大数(最大10,000件)
retstartESearch/EFetch0ページネーションのオフセット
rettypeEFetchfull出力タイプ:abstract、medline、xml、uilist
retmodeすべてxml出力形式:xml、json、text
sortESearchrelevanceソート順:relevance、pub_date、first_author
usehistoryESearchn履歴サーバーを有効化します。大量の検索結果には y を指定します
api_keyすべてなし10リクエスト/秒(なしの場合は3リクエスト/秒)で利用するためのNCBI APIキー
cmdELinkneighborリンクタイプ:neighbor、neighbor_score、prlinks
datetypeESearchpdat日付フィールド:pdat(出版日)、edat(Entrez)

ベストプラクティス

  1. 必ずAPIキーを使用します — NCBIに登録すると、毎秒3件ではなく10件のリクエストを送信できます
  2. 500件を超える検索結果には履歴サーバーを使用します — URLの長さ制限を回避でき、バッチ取得が可能になります
  3. レート制限を設けます — APIキーを使用する場合は time.sleep(0.1)、使用しない場合は time.sleep(0.34) を指定します
  4. 結果をローカルにキャッシュします — PubMedのデータは頻繁には変わらないため、応答をキャッシュしてAPI呼び出しを最小限に抑えます
  5. MeSH用語とフリーテキストを併用します — 網羅的に検索するには [mh] と [tiab] を組み合わせます:(diabetes mellitus[mh] OR diabetes[tiab])
  6. 検索方法を記録します — システマティックレビューでは、正確なクエリ、日付、検索結果数を記録します
  7. 構造化データには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=20retmax=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」テーブルにまとめられています。

参考資料

関連スキル

  • biopython — E-utilities用の高水準Pythonラッパー(Bio.Entrez)
  • openalex-database — 生物医学以外も対象とする、より広範な学術文献
  • literature-review — システマティックレビューの方法論とPRISMAフレームワーク

コメント