【保存版】Python Notion 操作 （notion-sdk-py編）

こんばんは、本日はPythonでNotionを操作する方法について解説します。
私も普段からNotionはかなり利用しており、蓄積した大量のデータを再利用したいと思い少し触ってました。

Notion公式ではTypeScript版のSDKは公開されていますが、Python版は公式からありませんでしたので、有志の方が公式TypeScriptを元にPython版SDKを作成してくれています。

以下です👋
https://github.com/ramnes/notion-sdk-py

辞書的に使えるに記事を作成しますので、是非いいねや保存お願いします！！
基本的に以下を組み合わせることで何でも作れると思います！

## 前提条件
### インテグレーションの作成
NotionAPIを利用するために、以下のサイトにアクセスしてインテグレーション新規作成をします。
https://www.notion.so/my-integrations

基本は社内でNotionデータを二次利用することになるので`インターナル`としておくのが無難です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/da325a9a-9443-0ed5-9e03-c6c67fa9408b.png)

以下のシークレット情報はNotionAPIを操作するのに必要ですので、大切に管理してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/1bdabd7c-085c-8cb1-d703-d2b92cf69bdd.png)

### インテグレーションの接続
APIで操作したいページには`コネクタ`を追加する必要があります。
下図のように作成したインテグレーションを操作したいページにコネクトしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/bcbb164e-e3aa-b2f3-945c-6bbc70ff649d.png)


### ライブラリのインストール

```shell
pip install notion-client
```

## 操作対象
今回はサンプルとして以下のデータベースをPythonで操作することを想定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/0a484aa5-34ed-39fa-35f4-38529fcf835f.png)

### データベースID
本データベースのリンクをコピーからURLを取得して、その中からデータベースIDを抽出します。
`?v=`の直前の32桁のIDとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/695b3ad5-2253-315a-deea-ed44b5778bdf.png)

### ページID
各レコードごとのページIDの取得も基本はデータベースIDの取得と同様です。
対象のページを開いて、リンクを取得して32桁のIDを取得します。

## 基本概念
APIで操作するときの基本となる概念としては以下を理解しておいてください。
NotionではPropertiesとChildrenの2つで構成されています。
- Properties : Title, カテゴリなどのページの付加情報
- Children : ページのメインコンテンツ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/9cfcab86-d9f0-c2b6-f244-d0bbecef0596.png)


## ソースコード
### インスタンスの生成

Clientインスタンス作成時に引数として、インテグレーションのシークレット情報を利用します。
基本的にシークレット情報などはソースコードに埋め込まず、envファイル等から読み込むようにしてください。

```python
import os
from notion_client import Client

token = os.environ.get("NOTION_TOKEN")
client = Client(auth=token) # token: インテグレーションのシークレット情報
```

### ワークスペースのユーザー一覧を取得する

:::note warn
以下の処理ではゲストユーザーの情報は取得できないことに注意が必要です。
:::

```python
def get_all_notion_users():
    response = client.users.list()
    return response
```

### データベースの全ての情報を取得する

```python
def read_notion_database(database_id):
    response = client.databases.query(
        **{
            "database_id": database_id,
        }
    )

    print(response)
    return response
```

### データベースの中からStatus列がDoneの情報のみを取得する

```python
def read_notion_database(database_id):
    response = client.databases.query(
        **{
            "database_id": database_id,
            "filter": {
                "property": "Status",
                "status": {
                    "equals": "Done"
                }
            }
        }
    )

    print(response)
    return response
```

### データベースに存在するレコードのページID一覧を取得する

```python
def read_pages_from_database(database_id):
    response = client.databases.query(
        **{
            "database_id": database_id,
        }
    )

    results = response["results"]
    page_ids = []
    for result in results:
        # print(f'page_id={result["id"]}')
        page_ids.append(result["id"])

    print(f"read_pages_from_database completed. (len={len(page_ids)})")
    return page_ids
```

### ページ内のコンテンツを全て取得する
ページのプロパティやコンテンツを全て取得するときに使用します。

```python
def get_page_content(page_id):
    response = client.blocks.children.list(
        **{
            "block_id": page_id,
            "page_size": 50
        }
    )

    return response
```


### 指定したページのTitleプロパティを取得する

```python
def get_page_title(page_id):
    response = client.pages.retrieve(
        **{
            "page_id": page_id,
            "properties": [
                "Title"
            ]
        }
    )

    return response["properties"]["Title"]["title"][0]["plain_text"]
```

### 指定したページのStatusプロパティの値を取得する
```python
def get_page_status_property(page_id):
    response = client.pages.retrieve(
        **{
            "page_id": page_id,
            "properties": [
                "Status"
            ]
        }
    )

    return response["properties"]["Status"]["status"]["name"]
```

### 指定したページのEmailプロパティを取得する

```python
def get_page_email_property(page_id):
    response = client.pages.retrieve(
        **{
            "page_id": page_id,
            "properties": [
                "Email"
            ]
        }
    )

    return response["properties"]["Email"]["email"]
```

### データベースに新規レコードを追加する
少しコード長くなります。
各プロパティで日本語を使用している場合はエンコードした文字列を使用する必要があります。

以下のようなページが追加されます。
応用しやすいように様々な種類のプロパティやページコンテンツでも良く使う要素を入れています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/fe1a66c7-abbf-34b4-85fa-3b205c5c940f.png)


```python
def add_row_to_notion_database(database_id):
    response = client.pages.create(
        **{
            "parent": { "database_id": database_id },
            "icon":{
                "emoji": 😂

            },
            # ページのプロパティを指定する
            "properties": {
                "Title": {
                    "title": [
                        {
                            "text": {
                                "content": "yyyyMMddHHmmss"
                            }
                        }
                    ]
                },
                "\u30ab\u30c6\u30b4\u30ea": { # カテゴリ
                    "multi_select": [
                        {
                            "name": "\u30ab\u30c6\u30b4\u30ea1", # カテゴリ1
                        },
                        {
                            "name": "\u30ab\u30c6\u30b4\u30ea2", # カテゴリ2
                        },
                        # 選択肢が存在しない場合は自動で新規作成される
                        {
                            "name": "\u30ab\u30c6\u30b4\u30ea3", # カテゴリ3
                            "color": "red"
                        },
                        # 自動作成時、カラーを設定しないときは自動で付与される
                        {
                            "name": "\u30ab\u30c6\u30b4\u30ea4", # カテゴリ
                        }
                    ]
                },
                "Status": {
                    "status": {
                        "name": "In progress", # Done, In progress, Not started
                    }
                },
                "\u30c1\u30a7\u30c3\u30af": { # チェック
                    "checkbox": True # False
                },
                "\u512a\u5148\u5ea6": { # 優先度
                    "select": {
                        # "id": "aa5f95d4-cb4c-42ef-a418-46257b73cb62",
                        "name": "\u9ad8", # 高
                    }
                },
                "Email": {
                    "email": "test@example.com"
                },
                "Note": {
                    "rich_text": [
                        {
                            "text": {
                                "content": f"test {get_current_time()}",
                            }
                        }
                    ]
                }
            },  # end properties
            # ページ内にコンテンツを追加する場合はchildrenを指定する
            "children" : [
                # toc block
                {
                    "object": "block",
                    "type": "table_of_contents",
                    "table_of_contents" : {}
                },
                # heading 1
                {
                    "object": "block",
                    "type": "heading_1",
                    "heading_1" : {
                        "rich_text": [
                            {
                                "text": {
                                    "content": "Heading 1"
                                }
                            }
                        ]
                    }
                },
                # heading 2
                {
                    "object": "block",
                    "type": "heading_2",
                    "heading_2" : {
                        "rich_text": [
                            {
                                "text": {
                                    "content": "Heading 2"
                                }
                            }
                        ]
                    }
                },
                # heading 3
                {
                    "object": "block",
                    "type": "heading_3",
                    "heading_3" : {
                        "rich_text": [
                            {
                                "text": {
                                    "content": "Heading 3"
                                }
                            }
                        ]
                    }
                },
                # link
                {
                "object": "block",
                "paragraph": {
                    "rich_text": [
                        {
                            "text": {
                                "content": "Here is ATD website.",
                                "link": {
                                    "url": "https://www.atd-net.com"
                                }
                            },
                            "href": "https://www.atd-net.com"
                        }
                    ],
                    "color": "blue"
                    }
                },
                # code block python
                {
                    "object": "block",
                    "type": "code",
                    "code": {
                        "caption": [],
                        "rich_text": [
                            {
                                "type": "text",
                                "text": {
                                    "content": "x = aaa\nprint(x)",
                                    "link": None
                                },
                            }
                        ],
                        "language": "python"
                    }
                },
            ]
        }
    )

    print("notion database create completed")
    print(response) # 追加した内容を表示する
```

### ページの一部のプロパティを編集
今回はStatus列を`Done`, 優先度列を`高`に変更します。
```python
def update_page_properties(page_id):
    response = client.pages.update(
        **{
            "page_id": page_id,
            "properties": {
                "Status": {
                    "status": {
                        "name": "Done", # Done, In progress, Not started
                    }
                },
                "\u512a\u5148\u5ea6": { # 優先度
                    "select": {
                        "name": "\u9ad8", # 高
                    }
                }
            }
        }
    )

    print("notion database update response")
    print(response)
```




## さいごに
以上、Pythonで操作するNotionでした！！
自由に組み合わせて快適な自社アプリを作ってみてください！

今回のコード作成には`GitHub Copilot`の力を借りながら実施しました。
今後開発者がコードを書くことはどんどん減ると思いますが、参考にしてみてください。

今後も良く使う処理などあれば随時追加していきます。