Notion API 使ってみた ~Twitterトレンド保存~

## 最初に
Notion API(公式のもの)を使って何か作りたいということで、Twitterのトレンドを取得して、トレンドのキーワードに関連するツイートを保存するプログラムを作りました。特に意味はありませんw
実際に動かしています→Notionのリンクは[こちら](https://terusibata.notion.site/Twitter-957fc60a92dc4c998e6e2e7d6ddfbc38)
※作成者など、一部の情報が非表示の場合があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/ea1cc7a7-b409-6a18-5742-5a7876fa0fdd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/47529408-821c-e51b-d7c8-d8930ef84808.png)

## 記事について
:::note alert
簡易な説明または一部のみの説明を行います。コピペのみでは動かない場合があります。全体のコードは、[こちら](https://github.com/terusibata/Twitter_TrendSaving)に置いていますが、初心者が書いたので見にくいです。
また、説明用に変更していることがあります。
:::

:::note info
**2022年7月10日現在の動作は確認済みです。**
参考程度に参照してください。
:::

:::note warn
開発言語は**Python**です。
:::

:::note warn
Notion APIのバージョン
[[API Referebce] Versioning](https://developers.notion.com/reference/versioning)
現在のバージョンは**2022-06-28**ですがこの記事での動作は**2022-02-22**です。
ヘッダーのバージョン情報を変更することで利用できます。
:::

## 1.Notion APIを利用するにあたって

<details><summary>インテグレーションの作成方法はここを開く</summary>

### 1-1.APIを利用するには以下のリンクを開きます。

https://www.notion.so/my-integrations

### 1-2.「新しいインテグレーションを作成する」をクリックする。
ちなみにモバイル画面には対応していないようで非常に見にくいのでPCをオススメするよ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/e398a340-b012-43cd-99fd-489a5191cc42.png)

### 1-3.基本情報を入力する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/7ddeafc7-8d5a-0544-cdbc-bb0e137b0f35.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/c163c755-de49-2ff5-43a9-eb86e9ab4ea5.png)

### 1-4.「送信」ボタンを押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/eb339f87-6da0-2406-2abd-726afe9c2348.png)

### 1-5.トークンを利用します。「表示」を押すと「コピー」できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/fa7735e6-4290-a6bb-1e96-a3dd18767299.png)

</details>

## 2.利用したいNotionのページを作成・インテグレーションと連携する

:::note info
「招待」をしたページ以外、プログラムで編集や読み取る権限はないから安心！
:::

### 2-1.新しいページを作成しましょう！タイトルはなんでもいいよ！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/3f00ed55-4bf1-b961-b555-bd350e832d36.png)

### 2-2.右上にある「共有」をクリックして「ユーザーやインテグレーションを追加...」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/7efe3b5a-b667-6a0c-82ab-00b8e62d07c5.png)

### 2-3.作成したインテグレーションがあるので「招待」する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/cf54b29b-67f5-c7e3-c4d2-a3a9b3c46d47.png)

### 2-4.以下の画像のように追加されていたら準備OK!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668564/e9d8f981-553b-c66d-4b06-abe9316df6f4.png)

## 3.tweepyを使ってみよう

:::note warn
この記事ではNtion APIがメインですのでTwitter apiの取得の仕方は省略します。
:::

### 3-1.API連携を行う

```python:API_key.py
import tweepy
 
cunsumer_key = "access keyを入力"
cunsumer_secret = "access secretを入力"
access_token = "access tokenを入力"
access_token_secret = "access token secretを入力"
 
# API連携用のコード
auth = tweepy.OAuth1UserHandler(cunsumer_key, cunsumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
```

### 3-2.トレンドの一覧取得する

```python:trends.py
#日本のWOEID
woeid = 23424856
#トレンド一覧取得
trends =  API_key.api.get_place_trends(woeid)
  
trends_top = []
for trend in trends[0]["trends"]:
  trends_top.append(trend["name"])
    
#今回は30件のみにしている
print(trends_top[:30])
```
結果として

```python:結果
["#トレンド1","#トレンド2",...,"#トレンド30"]
```
が取得できる。

### 3-3.ツイートの内容を取得する
:::note warn
tweepyではかなり多くのデータを取得できますが、不必要なデータがたくさんあるので自分の欲しいデータのみに変換しています。
:::
:::note info
今回利用するツイート内容は以下の通りです。
**本文、ユーザーname、ユーザーID、投稿日、リツイート数、いいね数、ハッシュタグ、プロフィール画像、ヘッダー画像、ツイートに含まれる画像(1～4枚)または動画(1本)のURL**
:::

```python:tweet_search.py
import tweepy
import time

def search(search_word=None):

  while True:
    try:
      # 検索条件の設定（リストで渡せば複数指定できる）
      search = f"{search_word}　-filter:retweets"
      tweet_max = 50
       
      # ツイートを取得してtweetsという変数に代入
      tweets = tweepy.Cursor(API_key.api.search_tweets, q=search, lang="ja").items(tweet_max)
      break
    except:
      print("ツイートの取得に失敗しました。再度リクエストします")
      time.sleep(5)
   
  # 必要な情報のみを格納する箱(=リスト)を作る
  tweet_data = {}
  tweet_datas = []
  # リストに必要な情報を入れていく
  for tweet in tweets:
    #本文
    tweet_data["title"]=tweet.text
    #ユーザーname
    tweet_data["username"]=tweet.user.name
    #ユーザーID
    tweet_data["userid"]=tweet.user.screen_name
    #投稿日
    tweet_data["date"]=str(tweet.created_at)
    #リツイート数
    tweet_data["retweet_count"]=tweet.retweet_count
    #いいね数
    tweet_data["favorite_count"]=tweet.favorite_count
    #ハッシュタグ
    if "hashtags" in tweet.entities.keys(): 
      hashtag_data = {}
      hashtags = []
      for hashtag in tweet.entities["hashtags"]:
        hashtag_data["name"] = hashtag["text"]
        hashtags.append(hashtag_data)
        hashtag_data = {}
      tweet_data["hashtags"] = hashtags 
    else:
      tweet_data["medias"] = []
    #プロフィール画像
    try:
      tweet_data["profile_image"] = tweet.user.profile_image_url_https
    except:
      tweet_data["profile_image"]="https://img.icons8.com/ios/250/000000/twitter.png"
    #ヘッダー画像
    try:
      tweet_data["banner"]= tweet.user.profile_banner_url
    except:
      tweet_data["banner"]= "https://img.icons8.com/ios/250/000000/twitter.png"
    #media保存
    try:
      if "media" in tweet.extended_entities.keys():
        media = {}    
        medias = []
        for media_url in tweet.extended_entities["media"]:
          media["type"] = media_url["type"]
          if media["type"] =="video":
            try:
              video_bitrate = 0
              for video_url in media_url["video_info"]["variants"]:
                if "bitrate" in video_url.keys():
                  
                  if video_url["bitrate"] > video_bitrate:
                    media["URL"] = video_url["url"]
                    video_bitrate = video_url["bitrate"]

              if video_bitrate == 0:
                tweet_data["medias"] = []
                break
            except:
              tweet_data["medias"] = []
              break
          else:
            media["URL"] = media_url["media_url_https"]
          medias.append(media)
          media = {} 
        tweet_data["medias"] = medias
      else:
        tweet_data["medias"] = []
    except:
      tweet_data["medias"] = []
    
    tweet_datas.append(tweet_data)
    tweet_data = {}
  
  # 取得したツイートの内容を出力
  return tweet_datas
```

結果の例(データの一部を変更しています)
```js:結果
{
    'title': 'トレンド一覧を取得して保存するプログラムを作りました\n #トレンド',
    'username': 'テルシバタ',
    'userid': 'ffffffffffff',
    'date': '2022-07-09 17:37:11+00:00',
    'retweet_count': 0,
    'favorite_count': 5,
    'hashtags': [{
        'name': 'トレンド'
    }],
    'profile_image': 'https://pbs.twimg.com/profile_images/143631243768000000/PFIEFFEFEF_normal.jpg',
    'banner': 'https://pbs.twimg.com/profile_banners/12979615240280000000/1634314342',
    'medias': [{
        'URL': 'https://pbs.twimg.com/media/ffffffffffffff.jpg',
        'type': 'photo'
    }, {
        'URL': 'https://pbs.twimg.com/media/eeeeeeeeeeeeee.jpg',
        'type': 'photo'
    }, {
        'URL': 'https://pbs.twimg.com/media/dddddddddddddd.jpg',
        'type': 'photo'
    }]
}

```

## 4.公式Ntion APIを使ってみよう
:::note warn
requestsとtimeとjson以外のライブラリは利用していません。
:::

:::note info
以下のコードは必須となります。
```python:必須項目
import requests #リクエストの際に利用
import time #リクエストする時間を調整するのに利用
import json #json形式に利用

def get_request_url(end_point):
    return f'https://api.notion.com/v1/{end_point}'

notion_api_key = "ここにNotionのトークンを貼り付ける"

headers = {"Authorization": f"Bearer {notion_api_key}",
           "Content-Type": "application/json",
           "Notion-Version": "2022-02-22"}
```
:::

### 4-1.ページのデータを取得する
#### 4-1-1.親ページにあるデータベースページを全部取得する
API Referenceはこちらのページとなります。

https://developers.notion.com/reference/get-block-children

```python:notion_search.py
def getpages(page_id=None):
  list = []
  datas = {}
  start_cursor = None
  
  while True:
    if start_cursor is None:
      response = requests.request('GET', url=get_request_url(f'blocks/{page_id}/children?page_size=100'), headers=headers)
    else:
      response = requests.request('GET', url=get_request_url(f'blocks/{id}/children?page_size=100&start_cursor={start_cursor}'), headers=headers)
    res = response.json()
  
    for data in res["results"]:
      try:
        #データベースの場合
        datas["title"] = data["child_database"]["title"]
        datas["id"] = data["id"]
      except:
        #データベースではない場合はスキップ
        continue
      list.append(datas)
      datas = {}
  
    time.sleep(0.3)
    if not res["has_more"]:
      break
    else:
      start_cursor = res["next_cursor"]

  return list
```

基本的に何ページでも取得できます。1秒に3回以上リクエストするとエラーになるので、少し間隔をあけているので少し時間がかかるかもしれません。
```js:結果
[
  {"title":"ページ1","id","c3540e1f-dd03-49e7-9430-1d074d475d2d"},
  {"title":"ページ2","id","c3540e1f-dd03-49e7-9430-1d074d475d2e"}
]
```

#### 4-1-2.データベースページのページを取得する
API Referenceはこちらのページとなります。

https://developers.notion.com/reference/post-database-query

```python:notion_search.py
def getdatabase(page_id=None):
  list = []
  datas = {}
  start_cursor = None
  create_json = {}
  
  while True:
    if start_cursor is None:
      response = requests.request('post', url=get_request_url(f'databases/{page_id}/query'), headers=headers,json=create_json,data=json.dumps({'page_size': 100}))
    else:
      response = requests.request('post', url=get_request_url(f'databases/{page_id}/query'), headers=headers,json=create_json,data=json.dumps({'page_size': 100, 'start_cursor': start_cursor}))
    res = response.json()
  
    for data in res["results"]:
      try:
        datas["title"] = data["properties"]["ページタイトル"]["title"][0]["plain_text"]
        datas["id"] = data["id"]
      except:
        continue
      list.append(datas)
      datas = {}
  
    time.sleep(0.3)
    if not res["has_more"]:
      break
    else:
      start_cursor = res["next_cursor"]

  return list
```
今回はタイトルしか使わないので「title」のみですが、データベースページのプロパティ情報は全て取得することができます。
```js:結果
[
  {"title":"データベースページ1","id","c3540e1f-dd03-49e7-9430-1d074d475d2d"},
  {"title":"データベースページ2","id","c3540e1f-dd03-49e7-9430-1d074d475d2e"}
]
```
### 4-2.子要素を追加する
API Referenceはこちらのページとなります。

https://developers.notion.com/reference/patch-block-children
#### 4-2-1.親ページにヘッダー2を書き込む

```python:notion_create.py
  def heading_2(block_id=None,content_text=None):
    create_json = {"children": [
        {
          "object": 'block',
          "type": 'heading_2',
          "heading_2": {
            "rich_text": [
              {
                "type": 'text',
                "text": {
                  "content": content_text,
                },
              },
            ],
          },
        }
    ]}
    requests.request('patch', url=get_request_url(f'blocks/{block_id}/children'), headers=headers, json=create_json)
    time.sleep(0.3)
    print("ヘッダー2入力完了")
```

#### 4-2-2.親ページにテキストを書き込む

```python:notion_create.py
  def text(block_id=None,content_text=None):
    create_json = {"children": [
        {
          "object": 'block',
          "type": 'paragraph',
          "paragraph": {
            "rich_text": [
              {
                "type": 'text',
                "text": {
                  "content": content_text,
                },
              },
            ],
          },
        }
    ]}
    requests.request('patch', url=get_request_url(f'blocks/{block_id}/children'), headers=headers, json=create_json)
    time.sleep(0.3)
    print("テキスト入力完了")
```

#### 4-2-3.区切り線を書き込む

```python:notion_create.py
  def divider(block_id=None):
    create_json = {"children": [
        {
          "object": 'block',
          "type": 'divider',
          "divider": {}
        }
    ]}
    requests.request('patch', url=get_request_url(f'blocks/{block_id}/children'), headers=headers, json=create_json)
    time.sleep(0.3)
    print("区切り線の書き込み完了")
```

#### 4-2-4.親ページに画像を埋め込む

```python:notion_create.py
  def image(block_id=None,img = None):
    create_json ={"children": [
        {
          "type": "image",
          "image": {
            "type": "external",
            "external": {
              "url": img
            }
          }
        }
    ]}
    requests.request('patch', url=get_request_url(f'blocks/{block_id}/children'), headers=headers, json=create_json)
    time.sleep(0.3)
    print("画像の埋め込み完了")
```

#### 4-2-5.親ページに動画を埋め込む

```python:notion_create.py
  def Video(block_id=None,Video = None):
    create_json ={"children": [
        {
          "type": "video",
          "video": {
            "type": "external",
            "external": {
              "url": Video
            }
          }
        }
    ]}
    requests.request('patch', url=get_request_url(f'blocks/{block_id}/children'), headers=headers, json=create_json)
    time.sleep(0.3)
    print("ビデオの埋め込み完了")
```

### 4-3.親ページにデータベースページを作成する
API Referenceはこちらのページとなります。

https://developers.notion.com/reference/create-a-database

関連ツイートの内容を保存するためのデータベースページを作成するコード

```python:notion_create.py
  def database(block_id=None,content_text=None):
    create_json = {
      "parent": {
          "type": "page_id",
          "page_id": block_id
      },
      "icon": {
      	"emoji": "📝"
      },
      "title": [
          {
              "type": "text",
              "text": {
                  "content": content_text,
                  "link": None
              }
          }
      ],
      "properties": {
          "本文": {
              "title": {}
          },
          "ユーザー名": {
              "rich_text": {}
          },
          "ユーザーID": {
              "rich_text": {}
          },          
          "投稿日": {
              "date": {}
          },
          "リツイート数": {
            "number": {
                "format": "number"
            }
          },
          "いいね数": {
            "number": {
                "format": "number"
            }
          },
          "ハッシュタグ": {
            "type": "multi_select",
            "multi_select": {
                "options": []
            }
          }
      }
    }
    response = requests.request('post', url=get_request_url('databases'), headers=headers, json=create_json)
    time.sleep(0.3)
    print(f'{response.json()["id"]}にデータベースを作成しました')
    return response.json()["id"]
```

### 4-3.データベースページにページを追加する
API Referenceはこちらのページとなります。

https://developers.notion.com/reference/post-page


```python:notion_create.py
  def database_page(block_id=None,tweet_data=None):
    
    #tweepyのデータを変換しているだけです
    tweet_data["date"] = tweet_data["date"].replace('00:00',"").replace('+',"Z").replace(' ',"T")
    
    create_json = {
    	"parent": { "database_id": block_id },
      "icon": {
        	"type": "external",
          "external": {
          	"url": tweet_data["profile_image"]
          }
      },
      "cover": {
        	"type": "external",
          "external": {
          	"url": tweet_data["banner"]
          }
      },
    	"properties": {
    		"本文": {
    			"title": [
    				{
    					"text": {
    						"content": tweet_data["title"]
    					}
    				}
    			]
    		},
    		"ユーザー名": {
    			"rich_text": [
    				{
    					"text": {
    						"content": tweet_data["username"]
    					}
    				}
    			]
    		},
        "ユーザーID": {
    			"rich_text": [
    				{
    					"text": {
    						"content": tweet_data["userid"]
    					}
    				}
    			]
    		},
        "投稿日": {
    			"date": {
            "start":tweet_data["date"]
          }
    		},
        "いいね数": {
          "number": tweet_data["favorite_count"]
        },
        "リツイート数": {
          "number": tweet_data["retweet_count"]
        },
    		"ハッシュタグ": {
    			"multi_select": tweet_data["hashtags"]
    		}
      }
    }
    response = requests.request('post', url=get_request_url('pages'), headers=headers, json=create_json)
    time.sleep(0.3)
    response_id = response.json()["id"]
    print(f'{block_id }のデータベースに新しいページを作成しました')
    return response_id
```

## 最後に

全体のコードを見たい方は以下のリンクからどうぞ

https://github.com/terusibata/Twitter_TrendSaving

実際に保存しているページは以下のリンクからどうぞ

https://terusibata.notion.site/Twitter-957fc60a92dc4c998e6e2e7d6ddfbc38