1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Notion APIを使ってデータベースからページの内容を得る

Last updated at Posted at 2024-11-10

Notion APIを使ってデータベースにアクセスして、ウェブサイトを表示する方法が、WordPressにとってかわるという内容の話をXの投稿で見かけました。
投稿ではBolt.newを使って作っていましたが、私はBolt.newの無料版のせいか上手くできませんでした。
それでNotion APIでデータベースの内容を得る方法や、記事の内容を得る方法を勉強しようと思いました。
今回は、備忘録の意味も込めて記事にしました。
コードは、GPT-4oを使って制作しました。

このコードで、Notion APIの動作確認ができます。

データベースから記事IDを得て記事の内容を得る

必要なパラメーター

こちらのページで、インテグレーションの作成をします。
https://www.notion.so/profile/integrations

データベースのIDの確認方法
下記の記事などを参考されると、分かりやすいです。
https://note.com/amatyrain/n/ncf7111f74ffa

コマンド

npmを使います。
Notion API SDKを使います。

#Notion API SDK for JavaScript
npm install @notionhq/client

特にフレームワークを使っていませんので、index.jsなどのファイル名で実行できます。

node index.js

const { Client } = require("@notionhq/client");
const fs = require("fs");

//インテグレーション
const notion = new Client({
  auth: "******************************",
});

//データベースID
const DATABASE_ID = "*****************";

// ページの本文(ブロック内容)を取得する関数(再帰的に子ブロックも取得)
async function getPageContent(pageId) {
    const response = await notion.blocks.children.list({
        block_id: pageId,
    });

    console.log("Fetching content for pageId:", pageId); // デバッグ用ログ
    console.log("Response:", response); // APIレスポンスを確認

    // 各ブロックが子ブロックを持つ場合、再帰的にその子ブロックも取得
    for (let block of response.results) {
        if (block.has_children) {
            block.children = await getPageContent(block.id);
        }
    }

    return response.results;
}

// ブロックからテキストを取得する関数(再帰的に子ブロックも処理)
function getTextFromBlock(block) {
    let textContent = "";

    console.log(`Processing block type: ${block.type}`); // ブロックタイプのログ出力

    // 各ブロックタイプに応じて `rich_text` フィールドから内容を抽出
    switch (block.type) {
        case "paragraph":
            textContent = block.paragraph.rich_text.map(textItem => textItem.plain_text).join("");
            break;
        case "heading_1":
            textContent = `<h1>${block.heading_1.rich_text.map(textItem => textItem.plain_text).join("")}</h1>`;
            break;
        case "heading_2":
            textContent = `<h2>${block.heading_2.rich_text.map(textItem => textItem.plain_text).join("")}</h2>`;
            break;
        case "heading_3":
            textContent = `<h3>${block.heading_3.rich_text.map(textItem => textItem.plain_text).join("")}</h3>`;
            break;
        case "to_do":
            const checked = block.to_do.checked ? "[x]" : "[ ]";
            textContent = `${checked} ${block.to_do.rich_text.map(textItem => textItem.plain_text).join("")}`;
            break;
        case "bulleted_list_item":
            textContent = `• ${block.bulleted_list_item.rich_text.map(textItem => textItem.plain_text).join("")}`;
            break;
        case "numbered_list_item":
            textContent = `${block.numbered_list_item.rich_text.map(textItem => textItem.plain_text).join("")}`;
            break;
        case "toggle":
            textContent = `<details><summary>${block.toggle.rich_text.map(textItem => textItem.plain_text).join("")}</summary>`;
            break;
        case "quote":
            textContent = `<blockquote>${block.quote.rich_text.map(textItem => textItem.plain_text).join("")}</blockquote>`;
            break;
        case "code":
            const language = block.code.language;
            textContent = `<pre><code class="${language}">${block.code.rich_text.map(textItem => textItem.plain_text).join("")}</code></pre>`;
            break;
        case "callout":
            textContent = `<div class="callout">${block.callout.rich_text.map(textItem => textItem.plain_text).join("")}</div>`;
            break;
        default:
            textContent = ""; // 未知のブロックタイプには対応せず、空白に設定
            break;
    }

    console.log(`Extracted text for block type ${block.type}: ${textContent}`); // 抽出されたテキストのログ出力

    // 子ブロックがある場合は再帰的に処理
    if (block.has_children && block.children) {
        textContent += "<ul>";
        for (const child of block.children) {
            textContent += `<li>${getTextFromBlock(child)}</li>`;
        }
        textContent += "</ul>";
    }

    // トグルブロックの場合、閉じタグを追加
    if (block.type === "toggle") {
        textContent += "</details>";
    }

    return textContent;
}

// データベースの内容を取得する関数
async function getDatabaseContent() {
    try {
        const response = await notion.databases.query({
            database_id: DATABASE_ID,
        });

        let htmlContent = `
            <html>
            <head>
                <title>Notion Database Content</title>
                <style>
                    table { width: 100%; border-collapse: collapse; }
                    th, td { padding: 8px; border: 1px solid #ccc; }
                    th { background-color: #f4f4f4; }
                </style>
            </head>
            <body>
                <h1>Notionデータベースの内容</h1>
                <table>
                    <tr>
                        <th>ページID</th>
                        <th>タイトル</th>
                        <th>内容</th>
                    </tr>
        `;

        for (const page of response.results) {
            const pageId = page.id;
            const title = page.properties["名前"]?.title?.[0]?.text?.content || "タイトル未設定";

            const contentBlocks = await getPageContent(pageId);
            let content = contentBlocks.map(block => getTextFromBlock(block)).join("");

            htmlContent += `
                <tr>
                    <td>${pageId}</td>
                    <td>${title}</td>
                    <td>${content}</td>
                </tr>
            `;
        }

        htmlContent += `
                </table>
            </body>
            </html>
        `;

        fs.writeFileSync("notion_database_content.html", htmlContent);
        console.log("HTMLファイル 'notion_database_content.html' が作成されました");

    } catch (error) {
        console.error('エラーが発生しました:', error);
    }
}

getDatabaseContent();


生成されたHTML

************** は、ページIDです。


            <html>
            <head>
                <title>Notion Database Content</title>
                <style>
                    table { width: 100%; border-collapse: collapse; }
                    th, td { padding: 8px; border: 1px solid #ccc; }
                    th { background-color: #f4f4f4; }
                </style>
            </head>
            <body>
                <h1>Notionデータベースの内容</h1>
                <table>
                    <tr>
                        <th>ページID</th>
                        <th>タイトル</th>
                        <th>内容</th>
                    </tr>
        
                <tr>
                    <td>************************************</td>
                    <td>page1</td>
                    <td></td>
                </tr>
            
                <tr>
                    <td>************************************</td>
                    <td>テスト</td>
                    <td>2024年11月9日</td>
                </tr>
            
                <tr>
                    <td>************************************</td>
                    <td>page2</td>
                    <td>1234</td>
                </tr>
            
                </table>
            </body>
            </html>
        

表示例

スクリーンショット 2024-11-10 185730-1.jpg

主要なブロックの一覧

Paragraph(段落)

内容の取り出し: block.paragraph.rich_textからテキストを取得します。

const text = block.paragraph.rich_text.map(textItem => textItem.plain_text).join("");

Heading 1, 2, 3(見出し1, 2, 3)

内容の取り出し: block.heading_1.rich_text(またはheading_2, heading_3)からテキストを取得します。

const text = block.heading_1.rich_text.map(textItem => textItem.plain_text).join("");

To-do(チェックリスト)

内容の取り出し: block.to_do.rich_textからテキストを取得し、block.to_do.checkedでチェック状態を確認します。

const text = block.to_do.rich_text.map(textItem => textItem.plain_text).join("");
const checked = block.to_do.checked ? "[x]" : "[ ]";

Bulleted List Item(箇条書き)

内容の取り出し: block.bulleted_list_item.rich_textからテキストを取得します。

const text = block.bulleted_list_item.rich_text.map(textItem => textItem.plain_text).join("");

Numbered List Item(番号付きリスト)

内容の取り出し: block.numbered_list_item.rich_textからテキストを取得します。

const text = block.numbered_list_item.rich_text.map(textItem => textItem.plain_text).join("");

Toggle(トグル)

内容の取り出し: block.toggle.rich_textからテキストを取得します。

const text = block.toggle.rich_text.map(textItem => textItem.plain_text).join("");

Quote(引用)

内容の取り出し: block.quote.rich_textからテキストを取得します。

const text = block.quote.rich_text.map(textItem => textItem.plain_text).join("");

Code(コード)

内容の取り出し: block.code.rich_textからテキストを取得し、block.code.languageで言語を確認します。

const text = block.code.rich_text.map(textItem => textItem.plain_text).join("");
const language = block.code.language;

Callout(コールアウト)

内容の取り出し: block.callout.rich_textからテキストを取得します。

const text = block.callout.rich_text.map(textItem => textItem.plain_text).join("");

まとめ

今回は、Notion APIからデータの取り出しができるように、コードを作りました。
今回のコードでページのタイトルやコンテンツのブロックの抽出ができましたので、ウェブサイトの表示ができるように加工も可能と思います。

Bolt.newなどのAIを使ったコーディング支援を使ってコードを書くのも良いのですが、仕組みを理解するためには、こうした方法で作っていくのも良いと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?