以下の内容はhttps://uepon.hatenadiary.com/entry/2025/05/18/003609より取得しました。


NotionのデータをPythonで操る技術:API連携で実現する情報管理の自動化へ

メモ、タスク、アイデア、参考資料...日々増え続ける情報の洪水に困ったことはありませんか?

  • メモアプリはたくさん使ってみたけど、続かない...🥲
  • 情報がバラバラで、必要な時に見つからない...😥
  • 同じ作業の繰り返しに時間を取られている...😭

こんな状況でもPythonNotionを組み合わせれば情報整理ができるようになります。

1年ほど前に以下のような記事を書いていたのですが、ボリューム的に少し物足りないと思ったので今回は少し味方を変えてバーションアップの内容を書いてみたいと思います😎

paiza.hatenablog.com

Python×Notionの組み合わせが効く理由

Python×Notionは組み合わせによってその良さがいきてきます。

  1. Notionの自由度の高さ … データベース、ノート、タスク管理など多機能なワークスペースを自分好みにカスタマイズ可能
  2. NotionのUI … NotionのUIは初心者でも比較的操作がしやすく、表示形式も豊富である点も大きな利点です。
  3. Pythonの豊富なライブラリと機能性 … データ処理、API連携、自動化など多様なライブラリが揃い、あらゆる処理に対応可能
  4. APIを介した相互連携 … NotionAPIを使ってPythonから操作できる

今回はNotionの基本からPythonとの連携方法を簡単にまとめています。

1. Notionってどんなツール?

「なんでもノート」としての魅力

Notionは単なるメモアプリやタスク管理ツールではありません。最も魅力的なのは、その「なんでもできる」柔軟性です。Notionは以下のツールの機能を併せ持った「オールインワン」のワークスペースと呼べるのかもしれません。何でもできるので、初心者にはわかりにくいという話もありますが😎

  • ノートアプリ
  • データベース
  • カレンダー
  • プロジェクト管理
  • ドキュメント作成
  • Wiki構築

上記の機能を1つのインターフェースで使える点が Notionの優れた点です。これらの機能はダッシュボードのように組み合わせられる点です。また、初心者にも優しいインターフェースでドラッグ&ドロップ操作など、初めて使う人でも迷わない設計になっている点もいいですね。

エンジニアがNotionを使う理由

エンジニアにとってもNotionは単なる情報整理ツール以上の存在になりうるのではないでしょうか。例えばこんなことも可能でしょう。(GitHubでもできますけどね🙄)

  1. サンプルコードなどの管理 … シンタックスハイライト付きでコードを保存・整理できる
  2. 技術文書の作成 … マークダウン形式での記述に対応し、整形されたドキュメントを簡単に作成可能
  3. プロジェクト管理 … タスクの進行状況をデータベースで管理しながら、詳細な仕様をページ内に記述できる
  4. ナレッジベースの構築 … チーム内の知識を階層的に整理し、検索可能な形で蓄積できる
  5. リソースの一元管理 … 参考記事、チュートリアル、書籍など学習リソースを体系的に整理できる

Notionが選ばれる理由は、これまで述べた「柔軟性」と「統合性」でしょう。様々なツールを行き来する煩わしさから解放されるのはとても助かります。

API連携

Notionの長所は、APIApplication Programming Interfaceによってさらに高まります。APIを通じて、外部のツールやサービスと連携できます。

Notion APIでできることの一例

  • 外部データの取り込み … ウェブに公開されている情報をNotionのデータベースに自動登録
  • データの自動更新 … 定期的にデータを更新し、最新状態を維持
  • 他のサービスとの連携 … GitHubSlackGoogleカレンダーなど他のツールとの情報連携
  • データの集計・分析 … 蓄積されたデータを集計して可視化

Pythonのようなプログラミング言語と組み合わせることで、さらに多くのことができるようになります。

2. PythonでNotionを自動操作してみよう

Pythonを使ってNotionを操作するための準備と基本的な使い方を解説してみます。

使用するライブラリ紹介

PythonからNotion APIを利用するには、いくつかのライブラリが用意されています。

PythonからNotion APIを操作するライブラリ例

  • request … 低レベルでNotion APIを直接操作したい場合
    • 自分でヘッダーやエンドポイントを設定したい場合に便利
    • シンプルで学習向きだが、APIの仕様変更への対応は自己管理が必要
    • インストール方法 … pip install requests

参考

paiza.hatenablog.com

  • notion-client … Notion公式が提供する公式Pythonライブラリ
    • シンプルで堅牢なAPIラッパー
    • Notionの仕様変更に追従しやすい
    • インストール方法 …  pip install notion-client

参考

github.com

  • notion-sdk-py … 公式SDKの別名

    • 基本的にはnotion-clientと同じ
    • インストール方法 ... pip install notion-sdk-py

参考

github.com

初めてであれば、公式のライブラリであるnotion-clientから始めるのではないでしょう。この記事でも、notion-client を使用してきます。

3. APIトークンの取得と接続準備

Notion APIを使うには、APIトークンが必要となります。以下の手順で取得しましょう

1. Notion Integration(インテグレーション)の作成

Notion Developers ページにアクセスし、 【+ 新しいインテグレーション】をクリック。

インテグレーションの名を設定(例:"Python Integration")し、関連ワークスペースを選択、種類を【内部】に設定して【保存】ボタンをクリック。

2. シークレットトークンのコピー

  1. 作成したインテグレーションのページで【設定】タブをクリック
  2. 表示された【内部インテグレーションシークレット】をコピー(※このトークンは決して公開しないでください!)

また、以下にも注意してください。 Notion APIで9/25以降に作られるトークンの接頭辞が "secret" から "ntn" に変更されます。 既存のトークンも引き続き使えるので、特に更新する必要はありません。

developers.notion.com

3. インテグレーションをページ・データベース(テーブル)に接続

Notionで連携したいページや・データベース(テーブル)を作成する。

ページの場合

Notionで【新規ページを作成】ボタンをクリックする。

【ページ名】を入力(今回はPython Pageとしています)します。

データベース(テーブル)の場合

【データベース】ボタンをクリックして、

【空のデータベース】をクリックします。


上記の作業でページやデータベースのテーブルが作成されます。テスト用に適当なデータを入れておくとよいでしょう

インテグレーションと連携したいページ・データベース(テーブル)を連携する

ページの右上の【⋯】をクリックし、メニューから【接続】を選択し、先ほど作成したインテグレーションを選択します。

アクセス許可のダイアログで【はい】をクリック

4. Pythonでの準備

まずは、Notionのライブラリをインストールします。

$ pip install notion_client

続いて、APIキーを使用するために、コードでは以下のように準備します。だだし、APIキーをコードには直接書かず、環境変数.envファイルを使用してAPIキーを格納してください。

APIキーの読み込み例

import os
from notion_client import Client

# 方法1: 環境変数からシークレットトークンを読み込む方法(推奨)
# 事前に export NOTION_TOKEN=your_token を実行
notion = Client(auth=os.environ["NOTION_TOKEN"])

# 方法2: .envファイルからシークレットトークンを読み込む方法(推奨)
# 事前に pip install python-dotenv を実行し、ライブラリをインストール
# プロジェクトのルートに .env ファイルを作成し、その中に NOTION_TOKEN=<your_token>を記述
from dotenv import load_dotenv
load_dotenv()  # .envファイルを読み込む
notion = Client(auth=os.environ.get("NOTION_TOKEN"))

# 方法3: 直接記述する方法(非推奨)
# notion = Client(auth="secret_...")

上記の使用例での.envファイルの例

NOTION_TOKEN=secret_your_token_here

これで、Notionのデータにアクセスする準備が整いました。

4. 簡単なデータ取得と書き込み

それでは、実際にPythonからNotionを操作してみましょう。

ページとデータベースへのアクセスの注意点

ページ内にデータベース(テーブル)を作った場合と単体のデータベースを作成した場合で若干の意味合いが異なります。これ知ってないとハマるかもしれません。

✅ページ内にデータベースのみが存在する場合

  • ページIDのでのアクセスを行ってもプロパティのアクセスしかできない(エラーが発生する)
  • 扱いとしては空のページとなる。
  • データベース以外の要素が追加されれば空の扱いはされない。

✅単体でデータベースが存在する場合

  • データベースIDでアクセスを行うことができる。
  • データベース以外の要素が追加されれば空の扱いはされない。

図に表すと以下のようになります。

ページ編

ページの情報を取得する

ページの情報を取得する際にはページIDを使用して、データにアクセスします。ページIDは、NotionページのURLから取得できます。

例えば、ページを開いているときのURLが https://www.notion.so/Python-Page-1f602b8bc47b801fb63dde9e458f96fc の場合には、ページIDはPython-Page-以降のの32byte分の値である 1f602b8bc47b801fb63dde9e458f96fc となります。

get_page.py

import os
from notion_client import Client

from dotenv import load_dotenv
load_dotenv()  # .envファイルを読み込む
notion = Client(auth=os.environ.get("NOTION_TOKEN"))

page_id = "<URL内に含まれるページID>" # URL内に含まれるページIDに変更してください

print(f"pageid: {page_id}")

# ページのメタデータとプロパティを取得
page = notion.pages.retrieve(page_id)
title = page.get("properties", {}).get("title", {}).get("title", [{}])[0].get("plain_text", "タイトルなし") if page.get("properties", {}).get("title", {}).get("title") else "タイトルなし"
print("ページタイトル:", title)
print("ページのURL:", page.get("url", "URLなし"))

# ページのプロパティを表示(タイトルのみ)
print("\n===== ページのプロパティ =====")
properties = page.get("properties", {})
for prop_name, prop_data in properties.items():
    prop_type = prop_data.get("type", "unknown")
    
    # タイトルのみ表示
    if prop_type == "title":
        prop_value = prop_data.get("title", [{}])[0].get("plain_text", "") if prop_data.get("title") else ""
        print(f"{prop_name} ({prop_type}): {prop_value}")

# ページのブロックコンテンツを取得(段落のみ)
print("\n===== ページのコンテンツ =====")
blocks = notion.blocks.children.list(page_id)

# 段落ブロックのテキストを抽出する関数
def get_paragraph_content(block):
    """段落ブロックのテキスト内容を取得する関数"""
    block_type = block.get("type", "")
    
    # 段落ブロックのみ処理
    if block_type == "paragraph":
        block_data = block[block_type]
        rich_text = block_data.get("rich_text", [])
        return " ".join([rt.get("plain_text", "") for rt in rich_text])
    
    return ""

# 段落ブロックのみを表示
for block in blocks.get("results", []):
    block_type = block.get("type", "")
    if block_type == "paragraph":
        content = get_paragraph_content(block)
        print(f"[paragraph] {content}")

先ほど作成したNotionのページPython Pageでは以下のような実行結果となりました。

$ python get_page.py
pageid: 1f602b8bc47b801fb63dde9e458f96fc
ページタイトル: Python Page
ページのURL: https://www.notion.so/Python-Page-1f602b8bc47b801fb63dde9e458f96fc

===== ページのプロパティ =====
title (title): Python Page

===== ページのコンテンツ =====
[paragraph] text1
[paragraph] テキスト2
[paragraph]

ページに新しいコンテンツを追加する

Notionのページに新しいコンテンツを追加するには、Notion APIblocks.children.append()メソッドを使用します。テキスト段落やその他のブロックを追加するためのコード例を示します。

add_page.py

import os
from notion_client import Client
from dotenv import load_dotenv

load_dotenv()  # .envファイルを読み込む
notion = Client(auth=os.environ.get("NOTION_TOKEN"))

page_id = "<URL内に含まれるページID>" # URL内に含まれるページIDに変更してください

# ページに新しいコンテンツを追加する
def add_content_to_page(page_id):
    # 段落ブロックを追加
    notion.blocks.children.append(
        block_id=page_id,
        children=[
            {
                "object": "block",
                "type": "paragraph",
                "paragraph": {
                    "rich_text": [
                        {
                            "type": "text",
                            "text": {
                                "content": "これは追加した新しい段落です。",
                            }
                        }
                    ]
                }
            }
        ]
    )
    print("新しい段落を追加しました。")

    # 見出しブロックを追加
    notion.blocks.children.append(
        block_id=page_id,
        children=[
            {
                "object": "block",
                "type": "heading_2",
                "heading_2": {
                    "rich_text": [
                        {
                            "type": "text",
                            "text": {
                                "content": "新しいセクション",
                            }
                        }
                    ]
                }
            }
        ]
    )
    print("新しい見出しを追加しました。")

    # 箇条書きを追加
    notion.blocks.children.append(
        block_id=page_id,
        children=[
            {
                "object": "block",
                "type": "bulleted_list_item",
                "bulleted_list_item": {
                    "rich_text": [
                        {
                            "type": "text",
                            "text": {
                                "content": "箇条書きの項目1",
                            }
                        }
                    ]
                }
            },
            {
                "object": "block",
                "type": "bulleted_list_item",
                "bulleted_list_item": {
                    "rich_text": [
                        {
                            "type": "text",
                            "text": {
                                "content": "箇条書きの項目2",
                            }
                        }
                    ]
                }
            }
        ]
    )
    print("箇条書きを追加しました。")

# 関数を実行
add_content_to_page(page_id)
print("コンテンツの追加が完了しました。")

実行すると以下のように表示されます。

$ python add_page.py
新しい段落を追加しました。
新しい見出しを追加しました。
箇条書きを追加しました。
コンテンツの追加が完了しました。

追加するブロックには以下を指定する事ができます。

ブロックタイプ 説明 JSONオブジェクトの例
paragraph 基本的なテキスト段落 { "type": "paragraph", "paragraph": { "rich_text": [{ "type": "text", "text": { "content": "テキスト内容" } }] } }
heading_1 大見出し(H1) { "type": "heading_1", "heading_1": { "rich_text": [{ "type": "text", "text": { "content": "大見出し" } }] } }
heading_2 中見出し(H2) { "type": "heading_2", "heading_2": { "rich_text": [{ "type": "text", "text": { "content": "中見出し" } }] } }
heading_3 小見出し(H3) { "type": "heading_3", "heading_3": { "rich_text": [{ "type": "text", "text": { "content": "小見出し" } }] } }
bulleted_list_item 箇条書き { "type": "bulleted_list_item", "bulleted_list_item": { "rich_text": [{ "type": "text", "text": { "content": "箇条書き項目" } }] } }
numbered_list_item 番号付きリスト { "type": "numbered_list_item", "numbered_list_item": { "rich_text": [{ "type": "text", "text": { "content": "番号付き項目" } }] } }
to_do チェックボックス { "type": "to_do", "to_do": { "rich_text": [{ "type": "text", "text": { "content": "タスク" } }], "checked": false } }
code コードブロック { "type": "code", "code": { "rich_text": [{ "type": "text", "text": { "content": "console.log('Hello');" } }], "language": "javascript" } }
quote 引用ブロック { "type": "quote", "quote": { "rich_text": [{ "type": "text", "text": { "content": "引用テキスト" } }] } }
callout コールアウト { "type": "callout", "callout": { "rich_text": [{ "type": "text", "text": { "content": "注意事項" } }], "icon": { "type": "emoji", "emoji": "💡" } } }
divider 区切り線 { "type": "divider", "divider": {} }
table テーブル 複雑なため下記補足参照
image 画像 { "type": "image", "image": { "type": "external", "external": { "url": "https://example.com/image.jpg" } } }
bookmark ブックマーク { "type": "bookmark", "bookmark": { "url": "https://www.example.com" } }
toggle トグル(折りたたみ) { "type": "toggle", "toggle": { "rich_text": [{ "type": "text", "text": { "content": "クリックで開閉" } }] } }

データベース編

データベースのデータを取得する

Notionのページに以下のようなデータベースのテーブルを作成しておいたとします。このデータベースへのアクセスはデータベースIDを使用して行います。

データベースのテーブルのURLを確認すると以下のような形式になっています。

https://www.notion.so/[ドメイン名(省略可)]/[DATABASE ID(32桁の文字列)]?v=[VIEW ID]

具体的には、以下のようなURLとなっています。

https://www.notion.so/1f602b8bc47b8097a475e1a362a1da8d?v=1f602b8bc47b809e87c2000cc1081d49

このうちドメインの後にある”/” と ”?” で囲まれた部分の32桁の部分がデータベースIDとなります。この値をコピーして控えておきます。この例であれば1f602b8bc47b8097a475e1a362a1da8dとなります。

データベースのデータ取得の例は以下のようになります。

get_db.py

import os
from notion_client import Client

# プロジェクトのルートの .env ファイルに NOTION_TOKEN=<your_token>を記述
from dotenv import load_dotenv
load_dotenv()  # .envファイルを読み込む
notion = Client(auth=os.environ.get("NOTION_TOKEN"))

# データベースIDを指定してデータベースの内容を取得
database_id = "<URL内に含まれるデータベースID>" # URL内に含まれるデータベースIDに変更してください

results = notion.databases.query(database_id)

# 結果を表示
print("名前")
print("-" * 30)

for page in results["results"]:
    # 名前(タイトル)の取得
    name = "(名前なし)"
    if page["properties"]["名前"]["type"] == "title":
        title_data = page["properties"]["名前"]["title"]
        if title_data and len(title_data) > 0:
            name = title_data[0]["plain_text"]
        
    print(f"{name}")

先ほど作成したNotionのデータベース(テーブル)Python DBでは以下のような実行結果となりました。

$ python get_db.py
名前
------------------------------
名前2
名前1

データベースに新しいアイテムを追加する

import os
from notion_client import Client

# プロジェクトのルートの .env ファイルに NOTION_TOKEN=<your_token>を記述
from dotenv import load_dotenv
load_dotenv()  # .envファイルを読み込む
notion = Client(auth=os.environ.get("NOTION_TOKEN"))

database_id = "<URL内に含まれるデータベースID>" # URL内に含まれるデータベースIDに変更してください

def add_and_display_database_items():
    # 追加するアイテムの名前
    new_item_name = "名前3"  # 追加したい名前を設定
    
    # 新しいページ(アイテム)を作成
    try:
        new_page = notion.pages.create(
            parent={"database_id": database_id},
            properties={
                "名前": {
                    "title": [
                        {
                            "text": {
                                "content": new_item_name
                            }
                        }
                    ]
                }
                # 他のプロパティがある場合は、ここに追加
            }
        )
        
        print(f"新しいアイテム '{new_item_name}' を追加しました。")
        print(f"ページID: {new_page['id']}")
        print(f"ページURL: {new_page['url']}")
    except Exception as e:
        print(f"追加中にエラーが発生しました: {e}")
        return
    
    # 更新後のデータベースの内容を取得して表示
    try:
        print("\n現在のデータベース内容:")
        print("名前")
        print("-" * 30)
        
        results = notion.databases.query(database_id)
        
        if len(results["results"]) == 0:
            print("データベースにはアイテムがありません。")
            return
            
        for page in results["results"]:
            # 名前(タイトル)の取得
            name = "(名前なし)"
            if page["properties"]["名前"]["type"] == "title":
                title_data = page["properties"]["名前"]["title"]
                if title_data and len(title_data) > 0:
                    name = title_data[0]["plain_text"]
                    
            # ページIDを短縮して表示
            page_id_short = page["id"].replace("-", "")[:16] + "..."
            
            print(f"{name} (ID: {page_id_short})")
            
    except Exception as e:
        print(f"データ取得中にエラーが発生しました: {e}")

# 関数を実行
add_and_display_database_items()

このプログラムを実行すると以下の様になります。

$ python add_DB.py
新しいアイテム '名前3' を追加しました。
ページID: 1f602b8b-c47b-81e4-817b-d1d00940a5d1
ページURL: https://www.notion.so/3-1f602b8bc47b81e4817bd1d00940a5d1

現在のデータベース内容:
名前
------------------------------
名前3 (ID: 1f602b8bc47b81e4...)
名前2 (ID: 1f602b8bc47b80af...)
名前1 (ID: 1f602b8bc47b801b...)

5. 応用アイディア

ここまでできれば、もう少し踏み込んだ活用例も考えられるでしょう。

連携アイデア 詳細
Slackとの連携 チームで共有されたSlackのメッセージや重要な議論を自動的にNotionのデータベースに保存し、検索可能なナレッジベースを構築する。
カレンダー同期 Google CalendarOutlookのイベントをNotionのデータベースと自動同期し、会議の詳細情報やフォローアップ事項をイベントページに追記できるようにする。
GitHubプロジェクト管理 GitHubのissueやプルリクエストの状態をNotionのプロジェクト管理データベースに自動反映させ、技術的なタスクと非技術的なタスクを一元管理する。
Webフォーム集約 Google Formsなどで集めたアンケート回答や申し込み情報を自動的にNotionのデータベースに取り込み、データの可視化や分析を行う。
ニュースクリッピング RSSフィードや特定のニュースサイトから業界関連のニュースを自動的に取得し、Notionのデータベースにまとめて社内ニュースレターとして共有する。

Python×Notionを組み合わせることで、やれる幅が大きくひろがると思います。まずは小さな自動化から始めてみるのもいいかも🤩

おわりに

PythonNotionを組み合わせることで自動化されたシステムを簡単に構築できるのがメリットだと思います。

また、Notionも最近のAIの発展とともに色々な機能が追加されていて、自分が1年前に知っていた情報からいろいろと仕様変更もあったようです。このあたりもチェックが必要かなと感じました。




以上の内容はhttps://uepon.hatenadiary.com/entry/2025/05/18/003609より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14