NCBIのAPIを用いたクローラー開発の備忘録2024/08/18ゆきこ

# NCBIのデータ取得の解説
```
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):
    # 複数のフォーマットで日付をパースし、"YYYY/MM/DD"形式に変換する
    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 APIを使って論文データを取得する関数
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のエンドポイント
    db = "pubmed"  # 使用するデータベースはPubMed
    retmode = "json"  # レスポンス形式はJSON

    # APIに送信するパラメータを辞書として定義
    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)  # APIリクエストを送信

    if response.status_code == 200:  # ステータスコードが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}

# 論文IDを使って、詳細データを取得する関数
def fetch_article_details(uid):
    base_url = f"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi"  # APIのエンドポイント
    db = "pubmed"  # 使用するデータベースはPubMed
    retmode = "xml"  # レスポンス形式はXML

    # APIに送信するパラメータを辞書として定義
    params = {
        "db": db,
        "retmode": retmode,
        "id": uid  # 取得する論文のID
    }

    response = requests.get(base_url, params=params)  # APIリクエストを送信
    if response.status_code == 200:  # ステータスコードが200（成功）なら
        return response.text  # XML形式でレスポンスを返す
    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")  # タイトル内のスペースをURLエンコード
    link = f"https://pubmed.ncbi.nlm.nih.gov/?term={term}"  # 論文検索用リンクを作成

    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のエンドポイント
    response = requests.get(url)  # APIリクエストを送信

    if response.status_code == 200:  # ステータスコードが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}

# メイン関数
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)  # PubMedデータを取得

    if "error" in result:  # エラーが含まれている場合
        print(result)  # エラーメッセージを表示
        return

    titles = []  # タイトルを格納するリスト
    links = []  # リンクを格納するリスト
    if "esearchresult" in result and "idlist" in result["esearchresult"]:  # 検索結果が存在する場合
        for uid in result["esearchresult"]["idlist"]:  # 取得した論文IDごとに
            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"]  # 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)  # データフレームを作成

    # Excelファイルの出力先を定義
    output_directory = os.path.join(os.path.expanduser('~'), 'Documents')
    output_file_path = os.path.join(output_directory, 'ncbi_api_result_info.xlsx')

    df.to_excel(output_file_path, index=False, engine='openpyxl')  # データフレームをExcelに出力

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

if __name__ == "__main__":
    main()  # メイン関数を実行

```
# 解説
:::
このコードは、NCBIのAPIを使用してPubMedデータベースから学術論文の情報を取得し、それをExcelファイルに保存するものです。以下にコードの主要部分を解説します。

1. 日付フォーマットの変換 (format_date 関数)
日付を複数の形式から統一された形式（%Y/%m/%d）に変換します。異なる日付形式を取り扱うための処理です。

2. PubMedデータの取得 (fetch_pubmed_data 関数)
esearch.fcgi エンドポイントを使って、指定されたパラメータ（ジャーナル名、出版日、巻号、著者など）に基づいてPubMedデータベースから論文情報を検索します。検索結果はJSON形式で返されます。

3. 論文の詳細取得 (fetch_article_details 関数)
efetch.fcgi エンドポイントを使って、特定の論文ID（UID）に対応する詳細情報を取得します。この情報はXML形式で返されます。

4. 論文タイトルとリンクの解析 (parse_article_details 関数)
取得したXMLデータから論文タイトルを抽出し、PubMedでの検索リンクを生成します。

5. Citmatch APIを使った追加データの取得 (fetch_citmatch_data 関数)
ジャーナル名、巻号、ページ番号を指定して、特定の論文に関連するデータを取得します。取得したデータは、他のデータと同様に論文の詳細を取得するために使われます。

6. メイン処理 (main 関数)
ユーザーからの入力を受け取り、上記の関数を使って論文情報を取得し、Excelファイルとして保存します。

保存と出力
取得した論文情報をデータフレームにまとめ、Excelファイルとして保存します。出力先はユーザーのドキュメントフォルダです。

このコードを使うことで、指定した条件に基づくPubMedの論文情報を簡単に取得し、管理することができます。

:::


# 詳細解説
:::note info
1. PubMedデータの取得 (fetch_pubmed_data)
この関数では、esearch.fcgiというエンドポイントを使って、指定した条件に基づいてPubMedデータベースから論文のIDリストを取得します。journal（ジャーナル名）、pdat（発行日）、volume（巻号）、issue（号数）、authors（著者）などの検索パラメータを使って、検索クエリを作成します。

コード解説:

base_urlにAPIのエンドポイントURLを設定。
paramsに検索条件をセットし、リクエストを送信。
レスポンスをJSON形式で受け取り、結果を返します。
2. 論文詳細の取得 (fetch_article_details)
取得した論文ID（UID）を使って、efetch.fcgiエンドポイントから論文の詳細情報（XML形式）を取得します。

コード解説:

論文IDをパラメータとしてリクエストを送信。
XML形式で詳細データを受け取り、返します。
3. 論文詳細の解析 (parse_article_details)
XMLデータから論文のタイトルを抽出し、そのタイトルを使ってPubMedの検索リンクを生成します。

コード解説:

ElementTreeを使ってXMLを解析し、ArticleTitle要素からタイトルを取得。
タイトルを使って検索リンクを作成。
4. CitMatchデータの取得 (fetch_citmatch_data)
ジャーナル名、巻号、ページ番号を使って、特定の論文に関するデータを取得します。

コード解説:

citmatch APIにリクエストを送り、結果をJSON形式で受け取ります。
5. データの保存とエクスポート
取得した論文タイトルとリンクをExcelファイルに保存します。pandasを使ってデータフレームを作成し、openpyxlを利用してExcelファイルを出力します。

コード解説:

取得したデータをpandasのDataFrameに格納し、Excelファイルに保存。
このように、このコードはNCBIのAPIを活用してPubMedからの情報を自動で取得・整理し、使いやすい形で保存することができます。
:::