NotionAPI経由でデータを取得 ~ イチ(以下)から学ぶNotionAPI×Google Apps Script【Day.3】
こんばんは、ねここです。
目がよくないぼくですが、コードを睨みすぎて目薬が捗っています。
さて、昨日はGASをいじくって、NotionAPIのインテグレーションさんにご挨拶するところまで進めました。
今日は実際にNotion上の実験台ページにアクセスして情報を取得するところをやっていきます。
(今回からたぶんかなりコードだらけになるので、興味ない人はさらっと流し見でもいいと思いますw)
ページ上のブロック一覧を取得する
なにからやるのが正解か難しいところですが、まずはページ上のブロック一覧を取得してみます。
ページの情報を取得するときは、エンドポイントに指定したのは[pages]でした。
const ENDPOINT_CHANGE_STATUS_PAGE = "https://api.notion.com/v1/pages/";
const URL = ENDPOINT_CHANGE_STATUS_PAGE + 'ec306a9649ab4a579e1661138bdc38be';
リファレンスによると、ブロックの情報を取得するときは[blocks]を使うようですね。
const ENDPOINT = "https://api.notion.com/v1/";
url = ENDPOINT + "blocks/" + PageID + "/children?page_size=100";
今日は実際に使用するいろんな要素のID(ページIDとかブロックIDとかデータベースIDとか)もScriptPropertyに入れて、コードから取得するようにしました。32バイトのコードをそのままコードに書くよりは見栄えいいだろうし、今後の使い勝手を考えるとこの書き方にしておいたほうがいいかなというところですね(最適解なのかどうかはわかりませんw)。
ということで書いたコードはこんな感じ。
const ENDPOINT = "https://api.notion.com/v1/";
const NOTION_TOKEN = PropertiesService.getScriptProperties().getProperty("NotionToken");
const PageID = PropertiesService.getScriptProperties().getProperty("PageID");
// myFunctionから開始
function myFunction() {
// 「ページ上のブロック情報をください」の申請書を作ってインテグレーションさんに提出する
res = getBlockList();
// 受け取ったJSON形式のページ情報を解析して配列に収納する
res_parse = JSON.parse(res);
// 受け取った情報を全部表示する
console.log("Reserved Data:", res_parse)
}
function getBlockList() {
// ブロック情報をもらう
// ページ全体の設置ブロック一覧を取得する場合はページIDを設定する
url = ENDPOINT + "blocks/" + PageID + "/children?page_size=100";
// インテグレーションさんにわたす申請書を作る
const OPTIONS = {
"method" : "GET",
"headers": {
"Content-type": "application/json",
"Authorization": "Bearer " + NOTION_TOKEN,
"Notion-Version": "2022-06-28",
}
};
// インテグレーションさん、申請書を提出いたします。よろしくお願いします。
return UrlFetchApp.fetch(url, OPTIONS);
}
これを昨日使った実験台ページに放ってみました。
無事、ブロックの情報をもらうことができました。
実際はここから必要な情報を抽出していろいろする感じになりますね。
今日ずっとこのJSON形式のデータとやりとりして、とても目が疲れましたw
それぞれのブロックから表示されているテキストの抽出もやってみました 。
// コールアウトの表題テキストを表示
console.log(res_parse["results"][0]["callout"]["rich_text"][0]["text"]["content"]);
// ページIDを表示しているテキストの色が変わるまでを表示
console.log(res_parse["results"][1]["paragraph"]["rich_text"][0]["text"]["content"]);
// データベースIDを表示しているテキストの色が変わるまで[0]と、色を変えたあと[1]を表示
console.log(res_parse["results"][2]["paragraph"]["rich_text"][0]["text"]["content"], res_parse["results"][1]["paragraph"]["rich_text"][1]["text"]["content"]);
ブロック一覧(results)の中の1番目(プログラムではよく0から始まる)のブロック(コールアウト)の中のリッチテキスト(書式情報を含むテキスト)のまとまりの中の1番目の情報の中のテキスト(text, content)の情報を表示。
長えぇ!w
という感じで、狙いの情報を抽出するのにかなり苦労しながらコードをかりかりしてました。
今後もずっとJSON形式と戦い続けることになるんでしょうね。
データベースから情報を取得する
さて、ブロック一覧を取得できたところで、次はNotionAPIを使う上で一番の醍醐味になるであろうデータベースとの戦いです。
まずテスト用のデータをいくつか作りました。
とりあえずこのデータを取得できるように頑張ってみることにします。
条件を指定せず、アイテムを全部取得する
まずはアイテム一覧を取得してみましょう。
データベースをいじるときのエンドポイントは[databases]です。わかりやすい。
const ENDPOINT = "https://api.notion.com/v1/";
const NOTION_TOKEN = PropertiesService.getScriptProperties().getProperty("NotionToken");
const DatabaseID = PropertiesService.getScriptProperties().getProperty("DatabaseID");
function myFunction() {
// 「データベースの情報をください」の申請書を作ってインテグレーションさんに提出する
res = getDBquery();
// 受け取ったJSON形式のページ情報を解析して配列に収納する
res_parse = JSON.parse(res);
// 取得した件数を表示
console.log("Length: " + res_parse["results"].length);
// 1件ずつ名前とジャンルを取得する
for(i=0; i<res_parse["results"].length; i++) {
title = res_parse["results"][i]["properties"]['名前']["title"][0]["plain_text"];
// マルチセレクトは複数要素が入る前提なのでループを回して整理する
genre_array = new Array();
for(j=0; j<res_parse["results"][i]["properties"]['ジャンル']["multi_select"].length; j++){
genre_array[j] = res_parse["results"][i]["properties"]['ジャンル']["multi_select"][j]["name"];
}
genre = genre_array.join(", ");
console.log(title, "(" + genre + ")");
}
}
function getDBquery() {
url = ENDPOINT + "databases/" + DatabaseID + "/query";
content_data = {
// フィルタなどをここに
}
}
}
// インテグレーションさんにわたす申請書を作る
const OPTIONS = {
"method" : "POST", // query投げるときはPOST
"headers": {
"Content-type": "application/json",
"Authorization": "Bearer " + NOTION_TOKEN,
"Notion-Version": "2022-06-28",
},
"payload": JSON.stringify(content_data)
};
// インテグレーションさん、申請書を提出いたします。よろしくお願いします。
return UrlFetchApp.fetch(url, OPTIONS);
}
基本的にはブロック一覧を取得した時と同じですがmethodがPOSTになります(実はGETでも動いたんですが、オフィシャルにはPOSTです)。
指定のデータベースに対して「一覧おくれー」とクエリ依頼を投げて、渡してもらった一覧を頑張って使える形に加工、という流れになります。
無事にデータベースの中身をもらって、名前とジャンルを成形して表示することができました。
(ちなみに一覧をもらうところまではわりとすぐできましたが、この形で出力するのに4時間かかりましたw)
条件を指定して、フィルタリング
実際に活用するとなれば、干渉が必要なアイテムを絞りこむことが必要になります。
これはNotionのデータベース上でフィルタ設定するのと同じような感覚で指定できるようです。JSONで指定しないといけないんですが……
function getDBquery() {
url = ENDPOINT + "databases/" + DatabaseID + "/query";
content_data = {
// フィルタなどをここに
"filter": {
"property":"ジャンル",
"multi_select": {
"contains": "RPG"
}
}
};
ジャンルに「RPG」が含まれているものをピックアップする、という条件を先ほどのコードに追加します。これは条件がひとつなので、まだシンプルです。複数になると一目でわかりにくくなりますね、きっと。
無事にジャンルにRPGを指定しているアイテム3つを抽出することができました。
基本的にはこのやりかたでフィルタを増やして更新を掛けたいアイテムを抽出する、ということになりそうです。ふむふむ。
今回は並び順はとくに気にしてなかったですが、ソート指定もきっとできるんでしょうね。明日調べよう。
ということで今日はここまで。日が変わっちゃった!
データベースからの抽出は目途が立ったので、次はフィルタリングして抽出したアイテムの更新ですね。これができればNotionAPIでやりたいなぁと思ってることがかなり実現できそうです。
NotionAPIを使ってやりたいこと
それなりにやれそうな目途が立ってきたので、そろそろぼくがやりたかったことをいくつか宣言しておこうと思います。
できるといいなぁ。
繰り返しで作成したページの日付プロパティに「今日”以外”」の日付、時間を設定する
繰り返し設定でページを生成するとき、日付プロパティに「作成した日の何日後」といった指定をすることは現状できません(…..よね?)
他の人がどうかはわからないのですが、ぼくが使うときは結構先の日付を自動で設定したいなぁと思うことがあります。
ぼくの場合ゲームのタスクをNotionで管理することが多いのですが、例えばウィークリーミッションを設定するとき、終了期限もプロパティで自動設定したい人です。
FF14で毎週土曜の21時に当選番号の抽選が行われる「ジャンボくじテンダー」というコンテンツがあります。これは抽選後1週間を経過するとたとえ1等が当たっていても紙屑になってしまうものになります。
もちろん1等はあたったことはないですが、交換期限を超えてしまい紙屑にしてしまったことが結構ありましてw
これを抽選が行われる土曜21時に繰り返し機能でアイテムを作成していますが、「終了まで」という項目は自動では入れられません。本当は1週間後の紙屑になる日時を入れたいんですよね。
もっとも「週1件だから手入力でいいやん」はそのとおりですw
けどせっかく繰り返す生成するのであれば手入力を挟まずに自動で設置されてほしいのはNotionユーザーのサガですし、そもそも日付プロパティはNotionの機能の中でもかなり「かゆいと思っても絶妙に手が届かない」ことが多い部分なので、ここをNotionAPIを活用してうまく捌くことができるようになればNotionの自由度がかなり増えそうな予感がしています。
テキストブロックの中身を一定間隔で書き換えたい
Notionのテキストブロックは、Notion単体では自動で書き換えることはできません(…..よね?)
ですがNotionAPIを使えば書き換えることができ….るはずです(明日以降確かめる)。
Notion大学でいろんな人の画面を見せてもらう機会があるのですが、「自分が好きな言葉」であったり「元気になる言葉」であったりを大きく表示して、自分を奮い立たせる、みたいなことをやっている人がいます。すごくいいなぁと思いますし、ぼくだったらこれを毎日リストからランダムに表示したいなって思ったりするわけです。
「聞いて、感じて、考えて」と表示されているところを、一定間隔でDBに入っている文言のどれかに書き換えるイメージですね。
アイデアとしてはチープかもですが、テキストブロックの中身を自在にいじることでできること、わりとありそうだなと思ってます。
ということで、明日以降はこの目標を実現するために必要なテクニックを重点的に調べて実践していこうと思います。
明日はFF14で固定パーティで攻略する日なので、あんまり時間取れないかもだけど(予防線)
今日のカバー画像
いつも女の子ばっかり出力してるので、たまにはってことで男の子を出力してみました。
相変わらず細かい部分のディテールはぐちゃぐちゃですがw、雰囲気はよいのでヨシッ。
NEXT