Данные из базы Notion по Api за 5 минут

от автора

Недавно дали мне задачку — сделать скрипт, который будет вытаскивать данные из базы данных Notion по API и загружать их в хранилище S3 в формате Parquet (автоматическая работа скрипта была на заказчике), при этом:

  • нужен был определенный порядок выгрузки столбцов, которые будет задавать заказчик самостоятельно

  • нужно было ограничить выгрузку данных типами столбцов, которые заранее оговорили с заказчиком

Полную версию проекта можно посмотреть у меня на GitHub. Ну а в статье речь пойдет только про выгрузку из Notion.


Итак, первым делом надо создать интеграцию, получить токен и подключить эту интеграцию на странице с базой данных в Notion:

  1. Создаем интеграцию → идем сюда https://www.notion.com/my-integrations → нажимаем + New integration → даем имя (например my_token_bd) → сохраняем токен

  1. Идем на страницу Notion с базой данных, к которой нужен доступ по API → жмем “…” правом верхнем углу

→ крутим вниз до + Add Connections → выбираем созданную интеграцию (по имени)

Ты великолепен! Сказала я себе, решив, что все будет просто)

Ты великолепен! Сказала я себе, решив, что все будет просто)

Если нужно подробнее → смотри доку по Api Notion.


Запрашиваем данные из базы Notion:

def fetch_database_items(notion, database_id: str) -> List[Dict]:     """     Запросить элементы базы данных из Notion.      :param database_id: Идентификатор базы данных в Notion.     :param notion: Экземпляр клиента для взаимодействия с API Notion.     :return: Список элементов базы данных.     """     response = notion.databases.query(database_id=database_id)     return response['results']

Чтобы посмотреть что внутри я закинула их в json и пошла лицезреть, что там выгрузилось.

Я думала, что сразу выгрузится то, что мне нужно, то, что я вижу в самой базе в Notion.

Но нет…там была гигантская json — ина, в которой были указаны все параметры базы…и даже цвет текста…

Пример (из-за размера показан не весь):

[     {         "object": "page",         "id": "a1b2c3d4-e5f6-7g8h-9i0j-1234567890ab",         "created_time": "2024-01-01T09:00:00.000Z",         "last_edited_time": "2024-01-05T09:00:00.000Z",         "created_by": {             "object": "user",             "id": "1a2b3c4d-5e6f-7g8h-9i0j-1234567890ab"         },         "last_edited_by": {             "object": "user",             "id": "1a2b3c4d-5e6f-7g8h-9i0j-1234567890ab"         },         "cover": null,         "icon": null,         "parent": {             "type": "database_id",             "database_id": "abcdef12-3456-7890-abcd-ef1234567890"         },         "archived": false,         "in_trash": false,         "properties": {             "Last edited by": {                 "id": "last_editor",                 "type": "last_edited_by",                 "last_edited_by": {                     "object": "user",                     "id": "1a2b3c4d-5e6f-7g8h-9i0j-1234567890ab"                 }             },             "Multi-select": {                 "id": "multi_select",                 "type": "multi_select",                 "multi_select": [                     {                         "id": "initiation",                         "name": "Initiation",                         "color": "gray"                     },                     {                         "id": "planning",                         "name": "Planning",                         "color": "brown"                     }                 ]             },             "Phone Number": {                 "id": "phone_number",                 "type": "phone_number",                 "phone_number": "1234567890"             },             "Select": {                 "id": "select",                 "type": "select",                 "select": {                     "id": "design",                     "name": "Design",                     "color": "blue"                 }             },             "Number": {                 "id": "number",                 "type": "number",                 "number": 1             },             "Last edited time": {                 "id": "last_edited_time",                 "type": "last_edited_time",                 "last_edited_time": "2024-01-05T09:00:00.000Z"             },             "Date": {                 "id": "date",                 "type": "date",                 "date": {                     "start": "2024-01-01",                     "end": "2024-01-10",                     "time_zone": null                 }             },             "Created time": {                 "id": "created_time",                 "type": "created_time",                 "created_time": "2024-01-01T09:00:00.000Z"             },             "Status": {                 "id": "status",                 "type": "status",                 "status": {                     "id": "not_started",                     "name": "Not started",                     "color": "default"                 }             },             "Text": {                 "id": "rich_text",                 "type": "rich_text",                 "rich_text": [                     {                         "type": "text",                         "text": {                             "content": "Overview of Project Alpha",                             "link": null                         },                         "annotations": {                             "bold": false,                             "italic": false,                             "strikethrough": false,                             "underline": false,                             "code": false,                             "color": "default"                         },                         "plain_text": "Overview of Project Alpha",                         "href": null                     }                 ]             },             "Email": {                 "id": "email",                 "type": "email",                 "email": "example@domain.com"             },             "Name": {                 "id": "title",                 "type": "title",                 "title": [                     {                         "type": "text",                         "text": {                             "content": "Project Alpha",                             "link": null                         },                         "annotations": {                             "bold": false,                             "italic": false,                             "strikethrough": false,                             "underline": false,                             "code": false,                             "color": "default"                         },                         "plain_text": "Project Alpha",                         "href": null                     }                 ]             }         },         "url": "https://www.notion.so/Project-Alpha-a1b2c3d4e5f67g8h9i0j1234567890ab",         "public_url": null     },     {         "object": "page",         "id": "b2c3d4e5-f6g7-h8i9-j0k1-2345678901bc",         "created_time": "2024-02-01T09:00:00.000Z",         "last_edited_time": "2024-02-05T09:00:00.000Z",         "created_by": {             "object": "user",             "id": "2b3c4d5e-6f7g-8h9i-0j1k-2345678901bc"         },         "last_edited_by": {             "object": "user",             "id": "2b3c4d5e-6f7g-8h9i-0j1k-2345678901bc"         },         "cover": null,         "icon": null,         "parent": {             "type": "database_id",             "database_id": "abcdef12-3456-7890-abcd-ef1234567890"         },         "archived": false,         "in_trash": false,         "properties": {             "Last edited by": {                 "id": "last_editor",                 "type": "last_edited_by",                 "last_edited_by": {                     "object": "user",                     "id": "2b3c4d5e-6f7g-8h9i-0j1k-2345678901bc"                 }             },             "Multi-select": {                 "id": "multi_select",                 "type": "multi_select",                 "multi_select": [                     {                         "id": "execution",                         "name": "Execution",                         "color": "green"                     },  и т.д.

Пошла искать, как можно просто вытащить нужное. Надеялась найти готовое решение, но увы. Писала в слак в сообщество разработчиков, которые в теме по Api Notion, ответ один:

«You need to format it yourself based on the different types of properties. «.

Получилось сделать так:

REQUIRED_PROPERTIES = [     'title', 'rich_text', 'date', 'select',     'multi_select', 'number', 'checkbox', 'url'     ]   def extract_properties(item: Dict) -> Dict:     """     Извлечь свойства элемента базы данных Notion.      :param item: Объект элемента базы данных Notion.     :return: Словарь с извлеченными свойствами элемента.     """     properties = item['properties']     extracted_data = {'id': item['id']}     property_types = {}      for prop_name, prop_values in properties.items():         prop_type = prop_values['type']          if prop_type in REQUIRED_PROPERTIES:             if prop_type == 'title' or prop_type == 'rich_text':                 extracted_data[prop_name] = get_text(prop_values[prop_type])                 property_types[prop_name] = 'string'             elif prop_type == 'date':                 start_date, end_date = get_date(prop_values.get('date', {}))                 extracted_data[f'{prop_name}_start'] = start_date                 extracted_data[f'{prop_name}_end'] = end_date                 property_types[f'{prop_name}_start'] = 'string'                 property_types[f'{prop_name}_end'] = 'string'             elif prop_type == 'select':                 extracted_data[prop_name] = get_select(prop_values['select'])                 property_types[prop_name] = 'string'             elif prop_type == 'multi_select':                 extracted_data[prop_name] = get_multi_select(                     prop_values['multi_select']                     )                 property_types[prop_name] = 'array'             elif prop_type == 'url':                 extracted_data[prop_name] = prop_values.get(prop_type)                 property_types[prop_name] = 'string'             elif prop_type == 'number':                 extracted_data[prop_name] = prop_values.get(prop_type)                 property_types[prop_name] = 'int64'             elif prop_type == 'checkbox':                 extracted_data[prop_name] = prop_values.get(prop_type)                 property_types[prop_name] = 'bool'      with open('property_types.json', 'w+') as f:         json.dump(property_types, f)     return extracted_data

* параметры начальной и конечной даты выгружаются отдельно, не в кортеж, такой был запрос заказчика

* property_types — словарь с типами свойств, нужен для создания схемы с нужным порядком

Ну и сами функции:

def get_text(text_object: List[Dict]) -> str:     """     Получить текст из объекта текста базы данных Notion.      :param text_object: Объект текста из базы данных Notion.     :return: Объединенный текст.     """      text = ''     for rt in text_object:         text += rt.get('plain_text')     return text   def get_date(date_object: Dict) -> tuple:     """     Получить дату или диапазон дат из объекта даты базы данных Notion.      :param date_object: Объект даты из базы данных Notion.     :return: Одиночная дата или кортеж с начальной и конечной датами.     """      start_date = date_object.get('start', None) if date_object else None     end_date = date_object.get('end', None) if date_object else None     return start_date, end_date   def get_select(select_object: Dict) -> Union[str, None]:     """     Получить имя выбранного элемента из объекта выбора базы данных Notion.      :param select_object: Объект выбора из базы данных Notion.     :return: Имя выбранного элемента или None, если объект отсутствует.     """      return select_object.get('name') if select_object else None   def get_multi_select(multi_select_object: List[Dict]) -> List[str]:     """     Получить список строк, объединяющих элементы выбора из базы данных Notion.      :param multi_select_object: Список объектов выбора из базы данных Notion.     :return: Список строк, объединяющих элементы выбора.     """      return [', '.join([item.get('name', '') for item in multi_select_object])]

После извлечения данные уже выглядели так (пример, выгружала в формат CSV):

id,Number,Name,Select,URL,id,Text,Multi-select,Date_start,Date_end a1b2c3d4-e5f6-7g8h-9i0j-1234567890ab,1,Project Alpha,Design,www.example.com/alpha,a1b2c3d4-e5f6-7g8h-9i0j-1234567890ab,Overview of Project Alpha,"['Initiation, Planning']",2024-01-01,2024-01-10 b2c3d4e5-f6g7-h8i9-j0k1-2345678901bc,2,Project Beta,Development,www.example.com/beta,b2c3d4e5-f6g7-h8i9-j0k1-2345678901bc,Details of Project Beta,"['Execution, Monitoring']",2024-02-15,2024-03-01 c3d4e5f6-g7h8-i9j0-k1l2-3456789012cd,3,Project Gamma,Testing,www.example.com/gamma,c3d4e5f6-g7h8-i9j0-k1l2-3456789012cd,Testing Phase of Project Gamma,"['Testing, Validation']",2024-03-10,2024-03-20 d4e5f6g7-h8i9-j0k1-l2m3-4567890123de,4,Project Delta,Launch,www.example.com/delta,d4e5f6g7-h8i9-j0k1-l2m3-4567890123de,Launch Phase of Project Delta,"['Launch, Review']",2024-04-01,2024-04-05 e5f6g7h8-i9j0-k1l2-m3n4-5678901234ef,5,Project Epsilon,Maintenance,www.example.com/epsilon,e5f6g7h8-i9j0-k1l2-m3n4-5678901234ef,Maintenance of Project Epsilon,"['Maintenance, Support']",2024-05-15,2024-05-20 

Таким образом можно выгружать различные свойства из базы данных Notion.
Пожалуйста, не судите строго – я только начинаю свой путь в разработке. Надеюсь, мой опыт кому-нибудь поможет) И вы делитесь своим, если есть что сказать по теме статьи)


ссылка на оригинал статьи https://habr.com/ru/articles/835370/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *