見出し画像

20【Python×クローラー開発】NCBI PubMed APIで文献検索と詳細取得を自動化する方法(初心者向け解説編)

こんにちは!TechCommitメンバーの友季子です(^-^)
今回は、PubMed APIを使って文献検索や詳細取得を効率的に行う方法について前に書いた記事より詳しく、まとめてみました。研究や学術文献の検索をよりスマートにしたい方、美容医療に興味がある方、Pythonを学んでいる方々に役立てていただければ幸いです。
この記事を読んで、ちょっとでもAPIを使った文献データの開発をやってみよう~と思っていただければハッピーです♪


完成コード(コメントなし)

import requests
import os
import pandas as pd
from datetime import datetime
from xml.etree import ElementTree as ET

def format_date(date_str):
    for fmt in ("%Y/%m/%d", "%Y年%m月%d日", "%Y年%m月", "%Y年"):
        try:
            return datetime.strptime(date_str, fmt).strftime("%Y/%m/%d")
        except ValueError:
            continue
    return date_str

def fetch_pubmed_data(journal=None, pdat=None, volume=None, issue=None, authors=None):
    base_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
    db = "pubmed"
    retmode = "json"

    params = {
        "db": db,
        "retmode": retmode,
        "term": f"{journal}[journal]" if journal else None,
        "mindate": pdat,
        "maxdate": pdat,
        "volume": volume,
        "issue": issue,
        "author": authors
    }

    filtered_params = {key: value for key, value in params.items() if value}
    response = requests.get(base_url, params=filtered_params)
    if response.status_code == 200:
        return response.json()
    else:
        print(f"Error: {response.status_code}")
        print(response.text)
        return {"error": f"Failed to fetch data, status code: {response.status_code}", "details": response.text}

def fetch_article_details(uid):
    base_url = f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi"
    db = "pubmed"
    retmode = "xml"

    params = {
        "db": db,
        "retmode": retmode,
        "id": uid
    }

    response = requests.get(base_url, params=params)
    if response.status_code == 200:
        return response.text
    else:
        print(f"Error fetching article {uid}: {response.status_code}")
        return None

def parse_article_details(xml_data):
    root = ET.fromstring(xml_data)
    title = root.findtext(".//ArticleTitle")
    term = title.replace(" ", "%20")  # スペースを%20に置き換え
    link = f"https://pubmed.ncbi.nlm.nih.gov/?term={term}"
    
    return title, link

def fetch_citmatch_data(journal, volume, page):
    url = f"https://pubmed.ncbi.nlm.nih.gov/api/citmatch/?method=field&journal={journal}&volume={volume}&page={page}"
    response = requests.get(url)
    if response.status_code == 200:
        return response.json()
    else:
        print(f"Error: {response.status_code}")
        print(response.text)
        return {"error": f"Failed to fetch data, status code: {response.status_code}", "details": response.text}

def main():
    print("PubMed Search")
    journal = input("Enter journal name (or leave blank): ")
    pdat = input("Enter publication date (YYYY/MM/DD, YYYY年MM月DD日, etc., or leave blank): ")
    volume = input("Enter volume (or leave blank): ")
    issue = input("Enter issue (or leave blank): ")
    authors = input("Enter authors (format: Surname Initial, or leave blank): ")

    if not (journal or pdat or volume or issue or authors):
        print("Error: At least one search parameter must be provided.")
        return

    result = fetch_pubmed_data(journal, pdat, volume, issue, authors)
    
    if "error" in result:
        print(result)
        return
    
    titles = []
    links = []
    if "esearchresult" in result and "idlist" in result["esearchresult"]:
        for uid in result["esearchresult"]["idlist"]:
            details = fetch_article_details(uid)
            if details:
                title, link = parse_article_details(details)
                titles.append(title)
                links.append(link)
    
    # Fetch additional data using citmatch API
    citmatch_result = fetch_citmatch_data("Front Immunol", "13", "826091")
    if "result" in citmatch_result and "uids" in citmatch_result["result"]:
        for item in citmatch_result["result"]["uids"]:
            uid = item["pubmed"]
            details = fetch_article_details(uid)
            if details:
                title, link = parse_article_details(details)
                titles.append(title)
                links.append(link)
    
    data = {
        'Title': titles,
        'Link': links
    }

    df = pd.DataFrame(data)

    output_directory = os.path.join(os.path.expanduser('~'), 'Documents')
    output_file_path = os.path.join(output_directory, 'ncbi_result_info_api.xlsx')

    df.to_excel(output_file_path, index=False, engine='openpyxl')

    print(f"Excelファイル '{output_file_path}' に保存されたよ!")

if __name__ == "__main__":
    main() #2024_08 :09_ncbiのデータ取得でAPI版成功_LINKの文字列を作成してデータとれる_テスト進めてPROに出す

詳細解説のコード

import requests  # HTTPリクエストを送信するためのライブラリ
import os  # OS関連の操作を行うためのライブラリ
import pandas as pd  # データ処理と分析のためのライブラリ
from datetime import datetime  # 日付と時間の操作を行うためのライブラリ
from xml.etree import ElementTree as ET  # XMLデータの解析を行うためのライブラリ

# 日付文字列を標準フォーマットに変換する関数
def format_date(date_str):
    # 使用する日付フォーマットのリスト
    for fmt in ("%Y/%m/%d", "%Y年%m月%d日", "%Y年%m月", "%Y年"):
        try:
            # 日付文字列をフォーマットに従って変換
            return datetime.strptime(date_str, fmt).strftime("%Y/%m/%d")
        except ValueError:
            # 変換に失敗した場合は次のフォーマットを試す
            continue
    # すべてのフォーマットで変換できなかった場合、元の文字列を返す
    return date_str

# PubMedデータを検索し、文献IDのリストを取得する関数
def fetch_pubmed_data(journal=None, pdat=None, volume=None, issue=None, authors=None):
    base_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"  # PubMed検索APIのURL
    db = "pubmed"  # 使用するデータベース名
    retmode = "json"  # レスポンス形式(json)

    # APIリクエストのパラメータを設定
    params = {
        "db": db,  # データベース名
        "retmode": retmode,  # レスポンス形式
        "term": f"{journal}[journal]" if journal else None,  # ジャーナル名で検索(ジャーナル名が指定されていない場合はNone)
        "mindate": pdat,  # 発行日の最小値
        "maxdate": pdat,  # 発行日の最大値
        "volume": volume,  # 巻号
        "issue": issue,  # 号数
        "author": authors  # 著者名
    }

    # Noneの値を持つパラメータを削除
    filtered_params = {key: value for key, value in params.items() if value}
    # HTTP GETリクエストを送信
    response = requests.get(base_url, params=filtered_params)
    if response.status_code == 200:
        # 成功した場合はレスポンスをJSON形式で返す
        return response.json()
    else:
        # エラーが発生した場合はエラーメッセージを表示
        print(f"Error: {response.status_code}")
        print(response.text)
        return {"error": f"Failed to fetch data, status code: {response.status_code}", "details": response.text}

# 文献IDを用いてPubMedから文献の詳細を取得する関数
def fetch_article_details(uid):
    base_url = f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi"  # PubMed詳細取得APIのURL
    db = "pubmed"  # 使用するデータベース名
    retmode = "xml"  # レスポンス形式(xml)

    # APIリクエストのパラメータを設定
    params = {
        "db": db,  # データベース名
        "retmode": retmode,  # レスポンス形式
        "id": uid  # 文献ID
    }

    # HTTP GETリクエストを送信
    response = requests.get(base_url, params=params)
    if response.status_code == 200:
        # 成功した場合はレスポンスをテキスト形式で返す
        return response.text
    else:
        # エラーが発生した場合はエラーメッセージを表示
        print(f"Error fetching article {uid}: {response.status_code}")
        return None

# XMLデータから文献の詳細を解析する関数
def parse_article_details(xml_data):
    root = ET.fromstring(xml_data)  # XMLデータをパース
    title = root.findtext(".//ArticleTitle")  # 文献のタイトルを取得
    term = title.replace(" ", "%20")  # スペースを%20に置き換え(URL用)
    link = f"https://pubmed.ncbi.nlm.nih.gov/?term={term}"  # PubMedのリンクを生成
    
    return title, link  # タイトルとリンクを返す

# Citmatch APIを使って文献情報を取得する関数
def fetch_citmatch_data(journal, volume, page):
    url = f"https://pubmed.ncbi.nlm.nih.gov/api/citmatch/?method=field&journal={journal}&volume={volume}&page={page}"  # Citmatch APIのURL
    response = requests.get(url)  # HTTP GETリクエストを送信
    if response.status_code == 200:
        # 成功した場合はレスポンスをJSON形式で返す
        return response.json()
    else:
        # エラーが発生した場合はエラーメッセージを表示
        print(f"Error: {response.status_code}")
        print(response.text)
        return {"error": f"Failed to fetch data, status code: {response.status_code}", "details": response.text}

# メイン関数
def main():
    print("PubMed Search")  # ユーザーへのメッセージ
    # ユーザーからの入力を受け取る
    journal = input("Enter journal name (or leave blank): ")  # ジャーナル名
    pdat = input("Enter publication date (YYYY/MM/DD, YYYY年MM月DD日, etc., or leave blank): ")  # 発行日
    volume = input("Enter volume (or leave blank): ")  # 巻号
    issue = input("Enter issue (or leave blank): ")  # 号数
    authors = input("Enter authors (format: Surname Initial, or leave blank): ")  # 著者名

    # 検索パラメータが提供されていない場合はエラーメッセージを表示
    if not (journal or pdat or volume or issue or authors):
        print("Error: At least one search parameter must be provided.")
        return

    # PubMedでデータを検索
    result = fetch_pubmed_data(journal, pdat, volume, issue, authors)
    
    if "error" in result:
        print(result)  # エラーがある場合は表示
        return
    
    titles = []  # 文献のタイトルを保存するリスト
    links = []  # 文献のリンクを保存するリスト
    # 検索結果にIDリストが含まれている場合
    if "esearchresult" in result and "idlist" in result["esearchresult"]:
        for uid in result["esearchresult"]["idlist"]:
            details = fetch_article_details(uid)  # 文献の詳細を取得
            if details:
                title, link = parse_article_details(details)  # 詳細からタイトルとリンクを抽出
                titles.append(title)  # タイトルをリストに追加
                links.append(link)  # リンクをリストに追加
    
    # Citmatch APIを使って追加情報を取得
    citmatch_result = fetch_citmatch_data("Front Immunol", "13", "826091")
    if "result" in citmatch_result and "uids" in citmatch_result["result"]:
        for item in citmatch_result["result"]["uids"]:
            uid = item["pubmed"]  # 文献IDを取得
            details = fetch_article_details(uid)  # 文献の詳細を取得
            if details:
                title, link = parse_article_details(details)  # 詳細からタイトルとリンクを抽出
                titles.append(title)  # タイトルをリストに追加
                links.append(link)  # リンクをリストに追加
    
    # データフレームを作成
    data = {
        'Title': titles,  # 文献のタイトル
        'Link': links  # 文献のリンク
    }

    df = pd.DataFrame(data)  # データフレームを作成

    # 保存先のディレクトリとファイルパスを設定
    output_directory = os.path.join(os.path.expanduser('~'), 'Documents')
    output_file_path = os.path.join(output_directory, 'ncbi_result_info_api.xlsx')

    # データフレームをExcelファイルとして保存
    df.to_excel(output_file_path, index=False, engine='openpyxl')

    print(f"Excelファイル '{output_file_path}' に保存されたよ!")  # 保存完了メッセージ

# スクリプトが直接実行された場合にmain関数を呼び出す
if __name__ == "__main__":
    main()

PubMedは、医療や生命科学の研究文献を検索するための重要なリソースです。ここでは、PubMed APIを使って文献の検索と詳細取得を行う方法について詳しく解説します。具体的には、次の3つのAPIを使います:

  1. PubMed検索API (esearch.fcgi)

  2. PubMed詳細取得API (efetch.fcgi)

  3. Citmatch API

この記事では、これらのAPIを利用して文献データを取得するPythonコードを解説します。

1. PubMed検索API (esearch.fcgi)

目的

PubMedデータベースから、指定した検索条件に基づいて文献のIDリストを取得します。

使用場所

fetch_pubmed_data 関数内で使用されます。

完成コード※コメントなし

import requests  # HTTPリクエストを送信するためのライブラリ

def fetch_pubmed_data(journal=None, pdat=None, volume=None, issue=None, authors=None):
    base_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"  # APIのURL
    db = "pubmed"  # 使用するデータベース
    retmode = "json"  # レスポンス形式(json)

    # リクエストのパラメータを設定
    params = {
        "db": db,
        "retmode": retmode,
        "term": f"{journal}[journal]" if journal else None,
        "mindate": pdat,
        "maxdate": pdat,
        "volume": volume,
        "issue": issue,
        "author": authors
    }

    # Noneの値を持つパラメータを削除
    filtered_params = {key: value for key, value in params.items() if value}
    response = requests.get(base_url, params=filtered_params)  # HTTP GETリクエストを送信
    if response.status_code == 200:
        return response.json()  # 成功した場合はJSON形式で返す
    else:
        print(f"Error: {response.status_code}")  # エラーコードを表示
        print(response.text)  # エラーメッセージを表示
        return {"error": f"Failed to fetch data, status code: {response.status_code}", "details": response.text}

解説

  • base_url: PubMed検索APIのエンドポイントです。

  • params: 検索条件を含むパラメータを設定します。例えば、ジャーナル名や発行日などです。

  • filtered_params: Noneの値を持つパラメータを削除して、リクエストを最適化します。

  • requests.get: 指定したパラメータでHTTP GETリクエストを送信します。

2. PubMed詳細取得API (efetch.fcgi)

目的

文献IDを使用して、PubMedから文献の詳細情報を取得します。

使用場所

fetch_article_details 関数内で使用され、parse_article_details 関数を通じて詳細情報が利用されます。

コード

import xml.etree.ElementTree as ET  # XMLデータの解析を行うためのライブラリ

def fetch_article_details(uid):
    base_url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi"  # APIのURL
    db = "pubmed"  # 使用するデータベース
    retmode = "xml"  # レスポンス形式(xml)

    # リクエストのパラメータを設定
    params = {
        "db": db,
        "retmode": retmode,
        "id": uid  # 文献ID
    }

    response = requests.get(base_url, params=params)  # HTTP GETリクエストを送信
    if response.status_code == 200:
        return response.text  # 成功した場合はXML形式で返す
    else:
        print(f"Error fetching article {uid}: {response.status_code}")  # エラーコードを表示
        return None

def parse_article_details(xml_data):
    root = ET.fromstring(xml_data)  # XMLデータをパース
    title = root.findtext(".//ArticleTitle")  # 文献タイトルを取得
    term = title.replace(" ", "%20")  # スペースを%20に置き換え
    link = f"https://pubmed.ncbi.nlm.nih.gov/?term={term}"  # PubMedのリンクを生成
    
    return title, link  # タイトルとリンクを返す


解説

  • base_url: PubMed詳細取得APIのエンドポイントです。

  • params: 文献IDを指定して、詳細情報を取得するためのパラメータです。

  • ET.fromstring: XMLデータをパースし、文献タイトルを取得します。

  • parse_article_details: 文献タイトルを整形し、PubMedのリンクを生成します。

3. Citmatch API

目的

特定のジャーナルや巻号に関連する文献情報を取得します。

使用場所

fetch_citmatch_data 関数内で使用されます。

コード

def fetch_citmatch_data(journal, volume, page):
    url = f"https://pubmed.ncbi.nlm.nih.gov/api/citmatch/?method=field&journal={journal}&volume={volume}&page={page}"  # APIのURL
    response = requests.get(url)  # HTTP GETリクエストを送信
    if response.status_code == 200:
        return response.json()  # 成功した場合はJSON形式で返す
    else:
        print(f"Error: {response.status_code}")  # エラーコードを表示
        print(response.text)  # エラーメッセージを表示
        return {"error": f"Failed to fetch data, status code: {response.status_code}", "details": response.text}

解説

  • url: Citmatch APIのエンドポイントです。ジャーナル名、巻号、ページ数を指定してリクエストします。

  • requests.get: APIにリクエストを送信し、文献情報を取得します。

4.メイン関数

目的

ユーザーからの入力を受け取り、PubMedとCitmatch APIを使って文献情報を取得し、結果をExcelファイルとして保存します。

コード

import os
import pandas as pd

def main():
    print("PubMed Search")
    # ユーザーからの入力を受け取る
    journal = input("Enter journal name (or leave blank): ")
    pdat = input("Enter publication date (YYYY/MM/DD, YYYY年MM月DD日, etc., or leave blank): ")
    volume = input("Enter volume (or leave blank): ")
    issue = input("Enter issue (or leave blank): ")
    authors = input("Enter authors (format: Surname Initial, or leave blank): ")

    if not (journal or pdat or volume or issue or authors):
        print("Error: At least one search parameter must be provided.")
        return

    result = fetch_pubmed_data(journal, pdat, volume, issue, authors)
    
    if "error" in result:
        print(result)  # エラーがある場合は表示
        return
    
    titles = []  # 文献タイトルを保存するリスト
    links = []  # 文献リンクを保存するリスト
    
    if "esearchresult" in result and "idlist" in result["esearchresult"]:
        for uid in result["esearchresult"]["idlist"]:
            details = fetch_article_details(uid)  # 文献の詳細を取得
            if details:
                title, link = parse_article_details(details)  # 詳細からタイトルとリンクを抽出
                titles.append(title)  # タイトルをリストに追加
                links.append(link)  # リンクをリストに追加
    
    citmatch_result = fetch_citmatch_data("Front Immunol", "13", "826091")
    if "result" in citmatch_result and "uids" in citmatch_result["result"]:
        for item in citmatch_result["result"]["uids"]:
            uid = item["pubmed"]
            details = fetch_article_details(uid)  # 文献の詳細を取得
            if details:
                title, link = parse_article_details(details)
                titles.append(title)
                links.append(link)
    
    # データフレームを作成し、Excelファイルとして保存
    data = {
        'Title': titles,
        'Link': links
    }

    df = pd.DataFrame(data)
    output_directory = os.path.join(os.path.expanduser('~'), 'Documents')
    output_file_path = os.path.join(output_directory, 'ncbi_result_info_api.xlsx')
    df.to_excel(output_file_path, index=False, engine='openpyxl')

    print(f"Excelファイル '{output_file_path}' に保存されたよ!")

if __name__ == "__main__":
    main()

解説

  • input: ユーザーから検索条件を受け取ります。

  • fetch_pubmed_data: 入力に基づいて文献IDを取得します。

  • fetch_article_details: 各文献IDに対して詳細情報を取得します。

  • parse_article_details: 詳細情報からタイトルとリンクを抽出します。

  • fetch_citmatch_data: Citmatch APIを使って追加の文献情報を取得します。

  • pandas: 収集したデータをデータフレームとしてまとめ、Excelファイルに保存します。

参考文献

おわりに

これで、PubMed APIを使った文献検索と詳細取得のプロセスが完成です。Pythonを使ってAPIからデータを取得し、分析する方法を学ぶ良い練習になりますよ!何かあなたのお役に立てば嬉しいです
それでは頑張ってくださいね、応援しております!

この記事が気に入ったらサポートをしてみませんか?