iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
生成式 AI

30 天效率革命:用 n8n + AI 打造專屬助手系列 第 8

Day 8|Markdown → Notion:打造論文筆記自動化流程

  • 分享至 

  • xImage
  •  

透過 Python 將 Markdown 轉換成 Notion Blocks 並寫入頁面

透過 HTTP 呼叫 API的方式來加入頁面比較複雜,但也更靈活,可以自定義一些操作,在 Notion 中,如果要完整保留 Markdown 的層級結構(標題、清單、程式碼區塊等),單純文字轉換不夠,需要先把 Markdown 解析成 Notion 語法。以下展示如何利用 n8n + Python 轉換程式,並呼叫 Notion API 將內容寫入指定頁面。


轉換流程

  1. 使用 Python 將 Markdown 解析為 Notion Blocks JSON。
  2. 在 n8n Workflow 中,透過 HTTP Request Node 將 Blocks 寫入 Notion 節點創建的頁面。
  3. 驗證成功後,即可在 Notion 看到完整結構的筆記。
    https://ithelp.ithome.com.tw/upload/images/20250922/20148979UjEHp2WZl9.png

步驟一:透過 Python 轉換 Markdown → Notion 語法

以下程式碼可在 n8n 的 Code Node (Python) 執行,將輸入的 Markdown 轉換為 Notion Blocks 格式。

md2notion

# n8n Code (Python) — Convert Markdown to Notion Blocks
# Input:  items[*].json.content.parts[0].text
# Output: items[*].json.blocks  → 可直接餵給 Notion API 的 children

import re

def _rt_text(s: str, *, bold=False, italic=False, code=False, link_url=None):
    o = {"type": "text", "text": {"content": s}}
    if link_url:
        o["text"]["link"] = {"url": link_url}
    ann = {}
    if bold: ann["bold"] = True
    if italic: ann["italic"] = True
    if code: ann["code"] = True
    if ann: o["annotations"] = ann
    return o

_inline_pattern = re.compile(
    r'(\*\*|__)(.*?)\1|(?<!\*)\*(?!\*)(.*?)\*|(?<!_)_(?!_)(.*?)_|`([^`]+)`|\[([^\]]+)\]\(([^)]+)\)'
)

def parse_rich_text(text: str):
    rich, last = [], 0
    for m in _inline_pattern.finditer(text):
        start, end = m.span()
        if start > last: rich.append(_rt_text(text[last:start]))
        if m.group(2): rich.append(_rt_text(m.group(2), bold=True))
        elif m.group(3): rich.append(_rt_text(m.group(3), italic=True))
        elif m.group(4): rich.append(_rt_text(m.group(4), italic=True))
        elif m.group(5): rich.append(_rt_text(m.group(5), code=True))
        elif m.group(6): rich.append(_rt_text(m.group(6), link_url=m.group(7)))
        last = end
    if last < len(text): rich.append(_rt_text(text[last:]))
    return rich or [_rt_text(text)]

def markdown_to_notion_blocks(markdown: str):
    lines, blocks, i = markdown.splitlines(), [], 0
    while i < len(lines):
        line = lines[i]
        if not line.strip(): i += 1; continue
        if line.startswith("```"):
            lang, i, code_lines = line[3:].strip() or "plain text", i+1, []
            while i < len(lines) and not lines[i].startswith("```"):
                code_lines.append(lines[i]); i += 1
            if i < len(lines) and lines[i].startswith("```"): i += 1
            blocks.append({"object":"block","type":"code","code":{"language":lang,"rich_text":[_rt_text("\n".join(code_lines))]}})
            continue
        if line.startswith("# "): blocks.append({"object":"block","type":"heading_1","heading_1":{"rich_text":parse_rich_text(line[2:])}}); i+=1; continue
        if line.startswith("## "): blocks.append({"object":"block","type":"heading_2","heading_2":{"rich_text":parse_rich_text(line[3:])}}); i+=1; continue
        if line.startswith("### "): blocks.append({"object":"block","type":"heading_3","heading_3":{"rich_text":parse_rich_text(line[4:])}}); i+=1; continue
        if line.startswith("> "):
            quote_lines=[line[2:]]; i+=1
            while i < len(lines) and lines[i].startswith("> "): quote_lines.append(lines[i][2:]); i+=1
            blocks.append({"object":"block","type":"quote","quote":{"rich_text":parse_rich_text("\n".join(quote_lines))}})
            continue
        if line.startswith("- "):
            while i < len(lines) and lines[i].startswith("- "):
                blocks.append({"object":"block","type":"bulleted_list_item","bulleted_list_item":{"rich_text":parse_rich_text(lines[i][2:])}})
                i+=1
            continue
        para=[line]; i+=1
        while i < len(lines) and lines[i].strip() and not any(lines[i].startswith(p) for p in ("```","# ","## ","### ","- ","> ")):
            para.append(lines[i]); i+=1
        blocks.append({"object":"block","type":"paragraph","paragraph":{"rich_text":parse_rich_text("\n".join(para))}})
    return blocks

output=[]
for item in items:
    text=item.get("json",{}).get("content",{}).get("parts",[{}])[0].get("text","") or ""
    blocks=markdown_to_notion_blocks(text)
    output.append({"json":{"blocks":blocks}})
return output

這段程式會將輸入的 Markdown 文字,轉換為 Notion API 可接受的 children 區塊格式。


步驟二:新增 HTTP Request Node

接著在 n8n 中新增 HTTP Request Node,用來把轉換後的 blocks 寫入上一篇介紹 Notion 節點所創建的頁面。

設定範例:

{
  "method": "PATCH",
  "url": "https://api.notion.com/v1/blocks/{{ $json.id.replaceAll('-','') }}/children",
  "nodeCredentialType": "notionApi",
  "sendBody": true,
  "specifyBody": "json",
  "jsonBody": {
    "children": {{ $('Md to Notion').item.json.blocks.toJsonString() }}
  }
}

HTTP node 設定


成果

執行流程後,即可在指定的 Notion 頁面中,看到完整保留 Markdown 結構的內容。

完成效果


參考資料


上一篇
Day7: Notion Node 快速上手:Notion Page 建立與內容同步
下一篇
Day 9 | 論文筆記自動化:第一階段回顧
系列文
30 天效率革命:用 n8n + AI 打造專屬助手9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言