PubMedから最新論文を自動で探して要約してくれるPython 3のコードを書いたので、仕様と使い方を解説します。
自分用に作ったものなので、お使いになる際には好きにカスタマイズしていただいて構いません。MITライセンスでGitHubに公開しています。
概要
処理は大まかに以下の流れで進行します。
- PubMedを指定ジャーナル&期間&キーワードで網羅的に検索
- 特殊文字などを処理
- キーワードの重みで関連度スコアを付与
- 最も関連度が高いものをOpenAIで要約
- 整形した情報をSlackに送信
- 重複を避けるためにDOIを保存
Slackに送信されるメッセージは以下のようになります。
注意点
- PubMedに登録されていない雑誌は指定できません。arXivやChemRxivなどの論文を取得するにはそれぞれのAPIを用いた改変が必要です
- 自動実行機能は搭載していないので、手動でスクリプトを走らせる必要があります。自動化はお好きにどうぞ
- Slack API、OpenAI API、NCBI APIのAPIキーが必要です。それぞれの機能を使わないのならば不要です
- Slackとの連携についてはここでは解説していません。偉大な先人たちの記事を参照してください
- Slackとの連携がめんどくさければ、その部分の処理を別のものに変えてください
依存ライブラリ
各自でインストールをお願いします。
pip install openai requests beautifulsoup4 lxml slack_sdk
仕様の詳細解説
1. PubMed検索
まず ジャーナル名・期間・キーワード を組み合わせて PubMed を検索します。
内部的には NCBI Entrez の esearch と efetch を組み合わせています。
詳細はこちら:https://www.ncbi.nlm.nih.gov/books/NBK25501/
-
ジャーナル絞り込み:
JOURNALS = ["Journal A", "Journal B", ...]
に列挙した誌名で検索(PubMed に登録されている誌名である必要があります)
指定できる論文の一覧はこちら:https://ftp.ncbi.nih.gov/pubmed/J_Medline.txt -
期間指定:
SEARCH_DAYS = 14
のように直近 N 日を対象にします(デフォルトは 14 日) -
ID 取得→詳細取得:まず
esearch
で PMID を取得し、次にefetch
で XML をまとめて取りにいく二段構えです(高負荷によるエラーを避けるため)。大量ヒット時はCHUNK_SIZE = 20
で 20 件単位に分割してefetch
します -
レート制御:API の制限を踏まえて
REQUEST_PAUSE = 0.1
秒のリクエスト間隔を入れています(必要に応じて伸ばしてください)
2. 特殊文字の処理
PubMed の XML には HTML エンティティや一部の制御文字が混ざることがあります。
そこで以下の前処理を入れています。
-
XML のクリーニング:文字参照に置き換えるなどして、不正文字でパースが落ちないようにします(
clean_xml
) -
テキスト整形:HTML エンティティの除去、余分な空白の圧縮、改行の正規化などを行い、タイトルや抄録を素直なプレーンテキストに整えます(
clean_text
) -
パーサ切り替え:BeautifulSoup / lxml で XML を安全にパースし、タグ欠落やフォーマットの揺れにもそこそこ耐えるようにしています(
parse_with_html_parser
)
3. 関連度スコアの計算
関連度は 重み付きキーワードの一致回数で評価します。
-
KEYWORDS = {"Keyword A": 10, "Keyword B": 5, "Keyword C": -5}
のように、
「強く拾いたい語(正の重み)」と「避けたい語(負の重み)」を同居させられます。 -
各キーワードの出現回数×重みでスコアを出し、もっともスコアの高い論文を「最有力候補」として採用します(
count_weighted_keywords
)
4. OpenAIによる要約
3で最大スコアを獲得した論文のアブストラクトを OpenAI API に渡して、短く読みやすい要約を生成します。
-
デフォルトのモデルは
AI_MODEL = "gpt-4o-mini"
です。そこまで複雑なことをさせているわけではないので、"gpt-4o-mini"
で十分と思いますが、他にもたくさんのモデルがあります:
https://platform.openai.com/docs/models -
プロンプトはこんな感じです:
prompt = ( f"Summarize the following abstract into three concise bullet points. Please prioritize scientific accuracy:\n\n{abstract}" )
科学的な正確性を優先しつつ、箇条書き 3 点で要約を作ってくれます。ここを日本語に変えたら出力も日本語になると思います。 -
極端に短い抄録(例:50語未満)の場合は、「要約不要」としてスキップします
5. Slackに送信
整形したテキストを Slack の指定チャンネルに投稿します(send_to_slack
)。
メッセージには タイトル、著者、ジャーナル名、発行日、要約(箇条書き)、DOIのリンク が出力されます。
Slack 連携を使わない場合は、出力先を標準出力やファイル保存に置き換えてください。
6. DOIの記録
重複通知を避けるために、取得した論文の DOI をローカルに記録します。
既読 DOI は load_saved_dois()
で読み込み、探索の候補から 既出の DOI を除外します。
新規 DOI は save_dois()
で追記保存します(デフォルトの保存先ファイル名は saved_dois.txt
)。
終わりに
改めて、初心者がChatGPTに手伝ってもらいながら書いたコードなので、不自然な箇所も多くあるかと思います。皆さんの手で好きに改変してください。