はじめに
研究業績は、意外といろいろな場所で使います。
- 個人Webサイト
- 研究室Webサイト
- Markdown形式のCV
- プレーンテキストの業績リスト
- researchmap
- 申請書や所内書類
業績を毎回用意するのが本当に面倒です。書類の中身に時間を割きたいのに、しょーもない体裁の調整に時間をかけたくありません。
そこでこの記事では、Notionのデータベースを研究業績の正本として管理し、PythonスクリプトでMarkdownやHTMLに出力する運用を紹介します。
Notionを入口にする理由として、
- よくできたGUIを活用したデータベースの構築ができる
- クラウド上にデータを保存できるのでPCの更新などに左右されない
- やろうと思えば今の他の環境からの移行もNotion APIでプログラムによる移行ができる(本記事では触れない)
あたりが理由です。
この記事は2本立ての前編です。
- 前編: Notion DBから業績リストをMarkdown/HTML/TXTに出力する
- 後編: Notion DBからresearchmap V2 CSVを生成・更新する
この記事では、researchmap連携にはまだ踏み込みません。まずは、Notionを正本にして業績リストを外部ファイルとして出力できるところまでを扱います。
この記事で作るもの
この記事で作る運用は、次のようなものです。
Notion Publications DB
↓
Pythonで取得
↓
cache/publications.json
↓
Markdown / HTML / TXT に出力
Notionを更新すれば、Webサイト用HTMLやMarkdown形式の業績リストを再生成できます。
例えば、次のようなコマンドでNotionから取得し、
uv run notion-export fetch \
--config config.yaml \
--cache cache/publications.json
次のようなコマンドでHTMLを出力します。
uv run notion-export render \
--config config.yaml \
--cache cache/publications.json \
--template publications.html.j2 \
--output outputs/publications.html
方針
この運用では、次の方針にします。
- Notion DBを正本にする
- 出力されたMarkdown/HTML/TXTは生成物として扱う
- フォーマット変更はテンプレート側で行う
- Notion APIの生データを直接テンプレートで触らない
- 一度、扱いやすい中間JSONに変換してから出力する
全体像は次の通りです。
Notion API
↓
normalize
↓
cache/publications.json
↓
Jinja2 template
↓
Markdown / HTML / TXT
この構成にしておくと、Notionから取得する処理と、出力フォーマットを調整する処理を分けられます。
必要なもの
この記事では、以下を使います。
- Notionアカウント
- Python
- uv
- Notion API integration
- Notion API token
- Notion database ID
Python環境管理には uv を使います。
Notion側の準備
Publications DBを作る
まず、Notionに業績管理用のデータベースを作ります。ここでは Publications という名前にします。
データベース構築の公式情報は、例えばこちらを参考してください。
この記事の方針では、Notion DBを単なるメモ置き場ではなく、Markdown、HTML、TXT、将来的にはresearchmap CSVなどに展開できる構造化された業績データベースとして使います。
そのため、最初から次のような入力欄を用意しておくと後で楽です。
基本情報
| プロパティ | 型 | 用途 | 入力例 |
|---|---|---|---|
Name |
title | 業績タイトル | Numerical study on ... |
Type |
select | 業績種別 |
Original Paper, 国内学会
|
Authors list |
relation | 著者リスト | Authors DBへのrelation |
Journal |
rich text / select | 雑誌名、会議名、リポジトリ名など |
Combustion and Flame, 日本燃焼学会
|
Publication date |
date | 出版日または発表日 | 2026-06-08 |
DOI |
url | DOI URL | https://doi.org/... |
Name, Type, Authors list, Journal, Publication date, DOI は、少なくとも用意しておくことをおすすめします。特に DOI は、論文・preprintの外部リンクとして使いやすく、HTML出力やresearchmap連携でも重要になります。
Type には、例えば次のような選択肢を用意します。
Original Paper
Review paper
Preprint
International conference
国内学会
Journal という名前は少し論文寄りですが、この記事では会議名やリポジトリ名も含む汎用的な「掲載先・発表先」として使います。気になる場合は Venue という名前にしてもよいです。
論文用の情報
論文をきちんと出力したい場合は、巻・号・ページも分けて持っておくと便利です。
| プロパティ | 型 | 用途 | 入力例 |
|---|---|---|---|
Volume |
number | 巻 | 315 |
Issues |
number | 号 | 1 |
Page |
rich text | ページまたは論文番号 |
112345, 123-130
|
Volume, Issues, Page は、論文リストとして表示するときにほぼ必須です。最近の論文ではページ範囲ではなくarticle numberだけの場合もあるので、Page は number型よりも rich text にしておく方が扱いやすいです。
学会発表用の情報
学会発表まで管理したい場合は、次のプロパティを追加します。
| プロパティ | 型 | 用途 | 入力例 |
|---|---|---|---|
Place |
rich text | 開催地 |
Houston, Texas, 東京
|
Duration |
date range | 会議の開催期間 |
2026-06-08 – 2026-06-10
|
Presentation |
select | 発表形式 |
Oral, Poster
|
Invited |
checkbox | 招待講演かどうか | checked / unchecked |
Tags |
multi-select | 任意の分類 |
combustion, quantum
|
Publication date は実際の発表日、Duration は会議全体の開催期間として使います。
Invited は、招待講演かどうかを後から出力に反映したい場合に重要です。researchmapでは「招待の有無」が独立した項目として扱われるため、後編のresearchmap CSV連携まで考えるなら追加しておくと便利です。
Preprint用の情報
arXivなどのpreprintも同じDBで管理したい場合は、次のプロパティを追加しておくとよいです。
| プロパティ | 型 | 用途 | 入力例 |
|---|---|---|---|
arXiv ID |
rich text | arXiv ID | 2501.01234 |
arXiv URL |
url | arXivページ | https://arxiv.org/abs/2501.01234 |
Journal Status |
select | 査読済み版との関係 |
preprint, under_review, published
|
Related Journal Paper |
relation | 対応する査読済み論文 | 同じPublications DBへのrelation |
preprintと査読済み版は、タイトルや著者順、DOIが変わることがあります。そのため、preprintとOriginal Paperは別レコードとして持ち、Related Journal Paper で紐づける運用が安全です。
将来のresearchmap連携まで考える場合
後編で扱うresearchmap CSV連携まで考えるなら、次のプロパティも追加しておくと後が楽です。
| プロパティ | 型 | 用途 |
|---|---|---|
Researchmap ID |
rich text | researchmap側の業績ID |
Researchmap Type |
select | researchmap種別を手動で上書きしたい場合 |
Sync to Researchmap |
checkbox | researchmapに出力するかどうか |
Researchmap Sync Status |
select | 照合・同期状態のメモ |
Language Code |
select |
jpn / eng
|
Country Code |
select / rich text |
JPN, USA, CAN など |
Visibility |
select |
disclosed, researchers_only, closed
|
Major |
checkbox | 主要業績かどうか |
ただし、この記事ではresearchmap連携には踏み込みません。まずは、MarkdownやHTMLに出すための基本項目が揃っていれば十分です。
最初に作るおすすめ構成
最初からすべての欄を用意するのが大変な場合は、まず以下の構成で始めるのがおすすめです。
| プロパティ | 型 |
|---|---|
Name |
title |
Type |
select |
Authors list |
relation |
Journal |
rich text / select |
Publication date |
date |
Volume |
number |
Issues |
number |
Page |
rich text |
DOI |
url |
Place |
rich text |
Duration |
date range |
Presentation |
select |
Invited |
checkbox |
Tags |
multi-select |
Authors DBを作る
著者は、文字列で直接持つより、別データベースにしてrelationでつなぐと管理しやすいです。
Authors DBを作り、最小構成では以下を用意します。
| プロパティ | 型 | 用途 |
|---|---|---|
Name |
title | 著者名 |
日本名 |
rich text | 日本語アイテムでの表示名 |
まずは Name だけでも十分です。必要に応じて、後から表示名やresearchmap用の表記を追加できます。
Publications DBとAuthors DBをrelationでつなぐ
relationは、別データベースの項目を参照するためのプロパティです。APIから見ると、Authors list には著者名そのものではなく関連先ページIDが入るため、スクリプト側でAuthors DBを読み直して著者名に変換しています。
Publications DBに Authors list というrelationプロパティを追加し、Authors DBを参照するようにします。
これにより、各業績に対して複数の著者を紐づけられます。Notion上で著者順を管理しておけば、出力時にもその順序を使えます。
このあたりの仕組みについては、公式ではこの辺に情報があります。
Notion API integrationを作る
Notion APIからデータベースを読むには、integrationを作成し、対象データベースへアクセス権を付ける必要があります。
大まかな手順は次の通りです。
- NotionのDeveloper向けページでinternal integrationを作る
- tokenを取得する
-
PublicationsDBにintegrationを追加する -
AuthorsDBにもintegrationを追加する -
database_idとauthors_db_idを取得する
NotionのDBを作っただけではAPIから読めません。DB側でintegrationに明示的に共有する必要があります。
環境変数を設定する
このプロジェクトでは、Notion API tokenやDB IDを環境変数で渡します。
direnv を使うと便利です。
direnv edit .
取得したNotion API TokenとDatabase IDを以下の環境変数に格納しておきます。
export NOTION_KEY="secret_xxxxxxxxxxxxxxxxx"
export database_id="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export authors_db_id="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.envrc には秘密情報が入るので、Git管理しないようにします。
.envrc
プロジェクトをセットアップする
以下のGitHubをクローンして持ってきてください。
git clone https://github.com/takakiba/notion-publication-exporter.git
cd notion-publication-exporter
おおよそ次のようなプロジェクト構造になっています。
notion-publication-exporter/
├── config.yaml
├── pyproject.toml
├── templates/
│ ├── publications.md.j2
│ ├── publications.html.j2
│ └── publications.txt.j2
├── src/
│ └── notion_exporter/
│ ├── __init__.py
│ ├── authors.py
│ ├── cache.py
│ ├── cli.py
│ ├── config.py
│ ├── dates.py
│ ├── models.py
│ ├── normalize.py
│ ├── notion_api.py
│ ├── render.py
│ ├── writer.py
│ └── researchmap/
└── README.md
依存関係は uv で入れます。
uv sync --default-index https://pypi.org/simple
CLIが見えるか確認します。
uv run notion-export --help
環境変数が読めているかも確認します。
uv run python - <<'PY'
import os
for key in ["NOTION_KEY", "database_id", "authors_db_id"]:
print(f"{key}: {'OK' if os.getenv(key) else 'MISSING'}")
PY
例えば次のように出ればOKです。
NOTION_KEY: OK
database_id: OK
authors_db_id: OK
config.yamlを書く
Notion側のプロパティ名は、人によって違います。そこで、Pythonコードにプロパティ名を直接書かず、config.yaml で対応関係を指定します。
例です。
notion:
token_env: NOTION_KEY
database_id_env: database_id
authors_database_id_env: authors_db_id
data_source_id_env: null
authors_data_source_id_env: null
query:
types:
- Original Paper
- Review paper
- Preprint
- International conference
- 国内学会
date_begin: null
date_end: today
date_order: descending
properties:
name: Name
title: Name
authors_relation: Authors list
type: Type
journal: Journal
venue: Journal
volume: Volume
issue: Issues
publication_date: Publication date
page: Page
doi: DOI
place: Place
duration: Duration
presentation: Presentation
tags: Tags
output:
template: publications.md.j2
file: outputs/publications.md
Notion側で Journal ではなく Venue というプロパティにしている場合は、ここを変えればよいです。
properties:
journal: Venue
venue: Venue
Notionからデータを取得する
Notionから最新データを取得し、正規化済みJSONとして保存します。
uv run notion-export fetch \
--config config.yaml \
--cache cache/publications.json
成功すると、例えば次のように表示されます。
Saved 70 item(s) to cache/publications.json
通常運用では、cache/publications.json を毎回上書きして問題ありません。別名で保存するのは、デバッグや比較をしたいときだけでよいです。
取得結果を確認するには、例えば次のようにします。
uv run python - <<'PY'
import json
from pathlib import Path
data = json.loads(Path("cache/publications.json").read_text(encoding="utf-8"))
print("items:", len(data))
for item in data[:5]:
print("-" * 80)
print("title:", item.get("title"))
print("type:", item.get("type"))
print("authors:", item.get("authors"))
print("venue:", item.get("venue"))
PY
Markdownに出力する
Markdown形式で出力します。
uv run notion-export render \
--config config.yaml \
--cache cache/publications.json \
--template publications.md.j2 \
--output outputs/publications.md
出力を確認します。
sed -n '1,80p' outputs/publications.md
フォーマットを変更したい場合は、基本的に templates/publications.md.j2 を編集します。Notionから再取得する必要はありません。テンプレートを編集したら、render だけ実行すればよいです。
HTMLに出力する
HTML形式で出力します。
uv run notion-export render \
--config config.yaml \
--cache cache/publications.json \
--template publications.html.j2 \
--output outputs/publications.html
HTML出力は、個人Webサイトや研究室Webサイトに埋め込む用途に向いています。
TXTに出力する
プレーンテキストで出力したい場合は、TXT用テンプレートを使います。
uv run notion-export render \
--config config.yaml \
--cache cache/publications.json \
--template publications.txt.j2 \
--output outputs/publications.txt
メールや申請書に貼り付けるときには、プレーンテキスト出力が便利です。
テンプレートを編集する
出力フォーマットを変えたい場合は、templates/ 以下の .j2 ファイルを編集します。
templates/
├── publications.md.j2
├── publications.html.j2
└── publications.txt.j2
例えば、Markdownで著者、タイトル、雑誌名、年を出すなら、テンプレートでは次のような書き方になります。
{% for section in sections %}
## {{ section.name }}
{% for item in section.records %}
- {{ item.authors | join(", ") }}, "{{ item.title }}", {{ item.venue }}, {{ item.year }}.
{% endfor %}
{% endfor %}
テンプレート側では、Notion APIの生データではなく、正規化済みの値を使います。
よく使う値は次のようなものです。
item.title
item.authors
item.type
item.venue
item.publication_date
item.year
item.volume
item.issue
item.pages
item.doi
item.place
item.duration_text_ja
item.duration_text_en
ハマりどころ
Notion DBを共有していない
integrationを作っても、DB側でそのintegrationにアクセス権を付けていないとAPIから読めません。
この場合、API側では「存在しない」ように見えることがあります。Publications DBと Authors DBの両方にintegrationを追加してください。
database_idが間違っている
NotionのURLからIDを取るときに、余計なクエリ文字列やハイフンの扱いで間違えることがあります。うまく読めない場合は、まずDB retrieveだけを試すと切り分けしやすいです。
relation先の著者名が取れない
Authors list をrelationにした場合、Publications DB側には関連先ページIDが入ります。著者名を出すには、関連先のAuthors DBページを読む必要があります。
この処理はスクリプト側で AuthorResolver のような形に分けておくと見通しがよいです。
フォーマット変更のために毎回fetchしてしまう
Notionのデータが変わっていないなら、毎回fetchする必要はありません。テンプレートの修正だけなら、既存の cache/publications.json を使って render だけ実行すれば十分です。
次回: researchmap CSV連携
この記事では、Notionを正本にしてMarkdown/HTML/TXTを出力するところまでを扱いました。
次回は、同じNotion DBからresearchmap V2 CSVを生成する運用を扱います。特に次の点を説明します。
- researchmap CSVの基本形式
-
Researchmap IDの管理 - 初回登録時の
insert/merge - 次回以降の
update/doc - researchmap export CSVとの照合
- Researchmap IDのNotionへの書き戻し
まとめ
Notionを研究業績の正本にしておくと、Webサイト、Markdown CV、プレーンテキストなどを同じデータから生成できます。
この運用のポイントは次の通りです。
- Notion DBを正本にする
- Notion APIで取得したデータを中間JSONにする
- 出力形式はJinja2テンプレートで管理する
- フォーマット変更時は
renderだけやり直す
研究業績は何度も使い回す情報なので、一度この流れを作っておくと、更新作業がかなり楽になります。
Python側のスクリプトは何度か作り直してきましたが、Notion Databaseを正本にしてAPI経由で取得・出力する、という骨組み自体は数年使ってみても安定しており、個人的にはかなり扱いやすい運用だと感じています。