本記事は富士通クラウドテクノロジーズ 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}
は認証トークン。
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( )」を参考にファイル出力を行う。
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)
{
"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の情報を取得する。
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のデータモデルを確認してみる。
{
"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情報は以下で取得できる。
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)
取得結果は以下。
{
"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
から確認した。
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)
{
"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
から確認した。
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)
{
"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を使用して追加してみる。
対象のエンドポイントが何かを確認してみる。
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アイテムを追加するときのプロパティごとの書き方
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)
まとめ
- Web APIの概念
- Requestsモジュールの使い方
- JSON形式のデータの扱い方
- APIのデータモデルの一例
が理解できた。
今後は、Notion APIを利用して、Notionと別アプリケーションの連携プログラムを作成していきたい。
その他参考資料
以下の書籍を参考にした。
明日は@hasunumaの「社内CTFのFJCTF#2を開催した話」です。
お楽しみに!