LoginSignup
2
1

Notion APIでWeb API操作を勉強してみた

Last updated at Posted at 2023-12-21

本記事は富士通クラウドテクノロジーズ Advent Calendar 2023 22日目の記事です。
昨日の記事は@ystkfujii人に運勢があるなら、HTTPリクエストにも運勢があるはず(謎でした。

本日は、理解できているようでよく理解できていないと日頃感じていたWeb API操作について勉強してみたという記事です。

Web APIとは?

Web APIという用語は聞いたことがあるが、どのように利用するがわかっていないので調べてみる。

そもそもWeb APIとは何かを調べてみた。

  • HTTPを使用してデータのやり取りをするAPI
    • URLでリソース位置を指定
    • GET、POSTなどのHTTPメソッドでデータのやり取りをする
    • HTTPヘッダ、HTTPボディにデータを載せて情報の転送を行う
    • 有名なWeb APIに、REST APIがある
    • REST APIは、URI(URL)に対してCRUD(Create,Read,Update,Delete)操作を行う
  • JSON形式でデータのやり取りすることが多い
  • 認証のために、トークンが必要なことが多い

Requestモジュールの使い方確認

今回はNotion APIを使用して、Web APIの操作を学ぶ。
以下を参考に、使用するためのインテグレーションを作成し、トークンを発行は準備済み。
Notion API を使用してデータベースを操作する

一番シンプルなbotユーザの取得を試してみる。
HTTPのヘッダーには、Notionのバージョン、認証トークン、Content-TypeにJSONを指定する。{api_token}は認証トークン。

RequestsTest.py
import requests
import json

url = 'https://api.notion.com/v1/users/me'
api_key = '{api_key}'

def RequestsTest(url, api_key):
    headers = {
        "Notion-Version": "2022-06-28",
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json"
    }

    response = requests.post(url, headers=headers)
    print(response)
    return

RequestsTest(url, api_key)

以下、実行結果。

<Response [200]>

あれ?APIの情報がJSON形式で返ってくるのでは?と思ったが、ステータスコードが返ってきた。

response.json()とするとJSON情報を確認できる。

{'object': 'user', 'id': '(記載省略)', 'name': 'notion_api', 'avatar_url': None, 'type': 'bot', 'bot': {'owner': {'type': 'workspace', 'workspace': True}, 'workspace_name': '○○さんのNotion'}, 'request_id': '(記載省略)'}

JSONモジュールの使い方確認

次に取得したJSONの情報をファイルに書き込む方法を調べてみる。
【Python】JSONの読書き|dump、dumpsの違い

response.json()で読み込んだものは<class 'dict'>である。
辞書型であるため、上記記事の「辞書型をJSON形式でファイルに出力する:json.dump( )」を参考にファイル出力を行う。

RequestsTestJsonDump.py
import requests
import json

url = 'https://api.notion.com/v1/users/me'
api_key = '{api_key}'

def RequestsTestJsonDump(url, api_key):
    headers = {
        "Notion-Version": "2022-06-28",
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json"
    }

    response = requests.get(url, headers=headers)
    json_dict = response.json()
    with open('getbotuser.json', mode='wt', encoding='utf-8') as f:
        json.dump(json_dict, f, ensure_ascii=False, indent=4)
    return

RequestsTestJsonDump(url, api_key)
getbotuser.json
{
    "object": "user",
    "id": "(記載省略)",
    "name": "notion_api",
    "avatar_url": null,
    "type": "bot",
    "bot": {
        "owner": {
            "type": "workspace",
            "workspace": true
        },
        "workspace_name": "○○さんのNotion"
    },
    "request_id": "(記載省略)"
}

JSONファイルを綺麗に表示する方法は以下を参考にした。
[python] JSONファイルのフォーマットを整えてDumpする

APIを実行してPageの情報を取得する

サンプルとして以下のPageを作成。
スクリーンショット 2023-12-09 18.18.30.png

Pageの情報を取得する。

GetPageObject.py
import requests
import json

url = 'https://api.notion.com/v1/pages/{page_id}'
api_key = {api_token}

def GetPageObject(url, api_key):
    headers = {
        "Notion-Version": "2022-06-28",
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json"
    }

    response = requests.get(url, headers=headers)
    json_dict = response.json()
    with open('getpage.json', mode='wt', encoding='utf-8') as f:
        json.dump(json_dict, f, ensure_ascii=False, indent=4)

    return

GetPageObject(url, api_key)

{page_id}はURLに記載されているもの。{api_token}はトークン情報。
取得したJSONの情報からNotion APIのデータモデルを確認してみる。

getpage.json
{
    "object": "page",
    "id": "(記載省略)",
    "created_time": "2023-12-06T16:38:00.000Z",
    "last_edited_time": "2023-12-09T08:48:00.000Z",
    "created_by": {
        "object": "user",
        "id": "(記載省略)"
    },
    "last_edited_by": {
        "object": "user",
        "id": "(記載省略)"
    },
    "cover": null,
    "icon": null,
    "parent": {
        "type": "workspace",
        "workspace": true
    },
    "archived": false,
    "properties": {
        "title": {
            "id": "title",
            "type": "title",
            "title": [
                {
                    "type": "text",
                    "text": {
                        "content": "プロジェクトタスク",
                        "link": null
                    },
                    "annotations": {
                        "bold": false,
                        "italic": false,
                        "strikethrough": false,
                        "underline": false,
                        "code": false,
                        "color": "default"
                    },
                    "plain_text": "プロジェクトタスク",
                    "href": null
                }
            ]
        }
    },
    "url": "https://www.notion.so/(記載省略)",
    "public_url": null,
    "request_id": "(記載省略)"
}

上記ではPageに作成したDatabase情報とBlock情報は見えない。
Database情報とBlock情報は以下で取得できる。

GetPageChidrenObject.py
import requests
import json

url = 'https://api.notion.com/v1/blocks/{page_id}/children'
api_key = '{api_token}'

def GetPageChidrenObject(url, api_key):
    headers = {
        "Notion-Version": "2022-06-28",
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json"
    }

    response = requests.get(url, headers=headers)
    json_dict = response.json()
    with open('getchildren.json', mode='wt', encoding='utf-8') as f:
        json.dump(json_dict, f, ensure_ascii=False, indent=4)

    return

GetPageChidrenObject(url, api_key)

取得結果は以下。

getchildren.json
{
    "object": "list",
    "results": [
        {
            "object": "block",
            "id": "(記載省略)",
            "parent": {
                "type": "page_id",
                "page_id": "(記載省略)"
            },
            "created_time": "2023-12-06T16:38:00.000Z",
            "last_edited_time": "2023-12-09T08:38:00.000Z",
            "created_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "last_edited_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "has_children": false,
            "archived": false,
            "type": "child_database",
            "child_database": {
                "title": "タスク一覧"
            }
        },
        {
            "object": "block",
            "id": "(記載省略)",
            "parent": {
                "type": "page_id",
                "page_id": "(記載省略)"
            },
            "created_time": "2023-12-09T06:05:00.000Z",
            "last_edited_time": "2023-12-09T08:39:00.000Z",
            "created_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "last_edited_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "has_children": false,
            "archived": false,
            "type": "child_database",
            "child_database": {
                "title": "メンバー"
            }
        },
        {
            "object": "block",
            "id": "(記載省略)",
            "parent": {
                "type": "page_id",
                "page_id": "(記載省略)"
            },
            "created_time": "2023-12-09T08:28:00.000Z",
            "last_edited_time": "2023-12-09T08:29:00.000Z",
            "created_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "last_edited_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "has_children": false,
            "archived": false,
            "type": "heading_1",
            "heading_1": {
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "今週のトピック",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "今週のトピック",
                        "href": null
                    }
                ],
                "is_toggleable": false,
                "color": "yellow_background"
            }
        },
        {
            "object": "block",
            "id": "(記載省略)",
            "parent": {
                "type": "page_id",
                "page_id": "(記載省略)"
            },
            "created_time": "2023-12-09T08:29:00.000Z",
            "last_edited_time": "2023-12-09T08:29:00.000Z",
            "created_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "last_edited_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "has_children": false,
            "archived": false,
            "type": "divider",
            "divider": {}
        },
        {
            "object": "block",
            "id": "(記載省略)",
            "parent": {
                "type": "page_id",
                "page_id": "(記載省略)"
            },
            "created_time": "2023-12-09T08:29:00.000Z",
            "last_edited_time": "2023-12-09T08:29:00.000Z",
            "created_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "last_edited_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "has_children": false,
            "archived": false,
            "type": "bulleted_list_item",
            "bulleted_list_item": {
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "あああああ",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "あああああ",
                        "href": null
                    }
                ],
                "color": "default"
            }
        },
        {
            "object": "block",
            "id": "(記載省略)",
            "parent": {
                "type": "page_id",
                "page_id": "(記載省略)"
            },
            "created_time": "2023-12-09T08:29:00.000Z",
            "last_edited_time": "2023-12-09T08:29:00.000Z",
            "created_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "last_edited_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "has_children": false,
            "archived": false,
            "type": "bulleted_list_item",
            "bulleted_list_item": {
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "いいいいい",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "いいいいい",
                        "href": null
                    }
                ],
                "color": "default"
            }
        },
        {
            "object": "block",
            "id": "(記載省略)",
            "parent": {
                "type": "page_id",
                "page_id": "(記載省略)"
            },
            "created_time": "2023-12-09T08:29:00.000Z",
            "last_edited_time": "2023-12-09T08:29:00.000Z",
            "created_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "last_edited_by": {
                "object": "user",
                "id": "(記載省略)"
            },
            "has_children": false,
            "archived": false,
            "type": "bulleted_list_item",
            "bulleted_list_item": {
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "ううううう",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "ううううう",
                        "href": null
                    }
                ],
                "color": "default"
            }
        }
    ],
    "next_cursor": null,
    "has_more": false,
    "type": "block",
    "block": {},
    "request_id": "(記載省略)"
}

https://api.notion.com/v1/blocks/{page_id}/childrenというエンドポイントにGETすることで、Paseにある全てのObjectが取得できる。

特定のDatabase情報を取得したい場合は、以下。
ここでは、タスク一覧を取得する。
{database_id}は、getchildren.jsonから確認した。

GetDatabaseObject.py
import requests
import json

url = 'https://api.notion.com/v1/databases/{database_id}'
api_key = '{api_token}'

def GetDatabaseObject(url, api_key):
    headers = {
        "Notion-Version": "2022-06-28",
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json"
    }

    response = requests.get(url, headers=headers)
    json_dict = response.json()
    with open('getdatabase.json', mode='wt', encoding='utf-8') as f:
        json.dump(json_dict, f, ensure_ascii=False, indent=4)

    return

GetDatabaseObject(url, api_key)
getdatabase.json
{
    "object": "database",
    "id": "(記載省略)",
    "cover": null,
    "icon": null,
    "created_time": "2023-12-06T16:38:00.000Z",
    "created_by": {
        "object": "user",
        "id": "(記載省略)"
    },
    "last_edited_by": {
        "object": "user",
        "id": "(記載省略)"
    },
    "last_edited_time": "2023-12-09T08:38:00.000Z",
    "title": [
        {
            "type": "text",
            "text": {
                "content": "タスク一覧",
                "link": null
            },
            "annotations": {
                "bold": false,
                "italic": false,
                "strikethrough": false,
                "underline": false,
                "code": false,
                "color": "default"
            },
            "plain_text": "タスク一覧",
            "href": null
        }
    ],
    "description": [],
    "is_inline": true,
    "properties": {
        "担当者": {
            "id": "(記載省略)",
            "name": "担当者",
            "type": "relation",
            "relation": {
                "database_id": "(記載省略)",
                "type": "single_property",
                "single_property": {}
            }
        },
        "ステータス": {
            "id": "(記載省略)",
            "name": "ステータス",
            "type": "status",
            "status": {
                "options": [
                    {
                        "id": "(記載省略)",
                        "name": "未着手",
                        "color": "default"
                    },
                    {
                        "id": "(記載省略)",
                        "name": "進行中",
                        "color": "blue"
                    },
                    {
                        "id": "(記載省略)",
                        "name": "完了",
                        "color": "green"
                    }
                ],
                "groups": [
                    {
                        "id": "(記載省略)",
                        "name": "To-do",
                        "color": "gray",
                        "option_ids": [
                            "(記載省略)"
                        ]
                    },
                    {
                        "id": "(記載省略)",
                        "name": "In progress",
                        "color": "blue",
                        "option_ids": [
                            "(記載省略)"
                        ]
                    },
                    {
                        "id": "(記載省略)",
                        "name": "Complete",
                        "color": "green",
                        "option_ids": [
                            "(記載省略)"
                        ]
                    }
                ]
            }
        },
        "工程": {
            "id": "(記載省略)",
            "name": "工程",
            "type": "multi_select",
            "multi_select": {
                "options": [
                    {
                        "id": "(記載省略)",
                        "name": "運用",
                        "color": "green"
                    },
                    {
                        "id": "(記載省略)",
                        "name": "テスト",
                        "color": "red"
                    },
                    {
                        "id": "(記載省略)",
                        "name": "開発",
                        "color": "default"
                    },
                    {
                        "id": "(記載省略)",
                        "name": "設計",
                        "color": "pink"
                    }
                ]
            }
        },
        "期日": {
            "id": "(記載省略)",
            "name": "期日",
            "type": "date",
            "date": {}
        },
        "名前": {
            "id": "title",
            "name": "名前",
            "type": "title",
            "title": {}
        }
    },
    "parent": {
        "type": "page_id",
        "page_id": "(記載省略)"
    },
    "url": "https://www.notion.so/(記載省略)",
    "public_url": null,
    "archived": false,
    "request_id": "(記載省略)"
}

特定のBlock情報を取得したい場合は、以下。

ここでは、コールアウトした"今週のトピック"の情報を取得する。
{block_id}は、getchildren.jsonから確認した。

GetBlockObject.py
import requests
import json

url = 'https://api.notion.com/v1/blocks/{block_id}'
api_key = '{api_key}'

def GetBlockObject(url, api_key):
    headers = {
        "Notion-Version": "2022-06-28",
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json"
    }

    response = requests.get(url, headers=headers)
    json_dict = response.json()
    with open('getblock.json', mode='wt', encoding='utf-8') as f:
        json.dump(json_dict, f, ensure_ascii=False, indent=4)

    return

GetBlockObject(url, api_key)
getblock.json
{
    "object": "block",
    "id": "(記載省略)",
    "parent": {
        "type": "page_id",
        "page_id": "(記載省略)"
    },
    "created_time": "2023-12-09T08:28:00.000Z",
    "last_edited_time": "2023-12-09T08:29:00.000Z",
    "created_by": {
        "object": "user",
        "id": "(記載省略)"
    },
    "last_edited_by": {
        "object": "user",
        "id": "(記載省略)"
    },
    "has_children": false,
    "archived": false,
    "type": "heading_1",
    "heading_1": {
        "rich_text": [
            {
                "type": "text",
                "text": {
                    "content": "今週のトピック",
                    "link": null
                },
                "annotations": {
                    "bold": false,
                    "italic": false,
                    "strikethrough": false,
                    "underline": false,
                    "code": false,
                    "color": "default"
                },
                "plain_text": "今週のトピック",
                "href": null
            }
        ],
        "is_toggleable": false,
        "color": "yellow_background"
    },
    "request_id": "(記載省略)"
}

Databaseを更新してみる

以下のような学習テーブルに、"深層学習入門"という行をAPIを使用して追加してみる。
スクリーンショット 2023-12-18 22.16.15.png

対象のエンドポイントが何かを確認してみる。
https://developers.notion.com/reference/update-a-database

Interacting with database rows
This endpoint cannot be used to update database rows.

To update the properties of a database row — rather than a column — use the Update page properties endpoint. To add a new row to a database, use the Create a page endpoint

公式リファレンスに上記の記載があり、Pageのエンドポイントを対象にPOSTをリクエストする必要がわかる。

レコードの追加方法とHTTPリクエストボディに記載する情報は以下にを参考にした。
Notion DataBaseをAPIから操作する際の用途別リクエスト構成についてまとめてみた
Notion APIからDBアイテムを追加するときのプロパティごとの書き方

CreateDatabaseRow.py
import requests

url = 'https://api.notion.com/v1/pages/'
api_key = '{api_key}'

def CreateDatabaseRowObject(url, api_key):
    headers = {
        "Notion-Version": "2022-06-28",
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json"
    }
    item_data = {
        "parent": {"database_id": "{database_id}"},
        "properties": {
            "ジャンル": {
                "multi_select": [
                    {
                        "name": "AI"
                    },
                ]
            },
            "チェックボックス": {
                "checkbox": False
            },
            "名前": {
                "title": [
                    {
                        "text": {
                            "content": "深層学習入門"
                        }
                    }
                ]
            }
        }
    }
    response = requests.post(url, headers=headers, json=item_data)
    return

CreateDatabaseRowObject(url, api_key)

追加することができた。
スクリーンショット 2023-12-18 22.17.49.png

まとめ

  • Web APIの概念
  • Requestsモジュールの使い方
  • JSON形式のデータの扱い方
  • APIのデータモデルの一例

が理解できた。
今後は、Notion APIを利用して、Notionと別アプリケーションの連携プログラムを作成していきたい。

その他参考資料

以下の書籍を参考にした。

明日は@hasunumaの「社内CTFのFJCTF#2を開催した話」です。
お楽しみに!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1