📖

NotionでエクスポートしたMDファイルをObsidianに取り込む

に公開

Obsidianに記事を移したかったため、スクリプトで対応することにしました。この作業を頻繁に行っていて、画像が10枚も20枚もあると、全て手動では無理なので、楽にやりたいなと思って作りました。(スクリプト化してるけど、ある程度は手動で整形します)

↓Obsidianはこの時の環境
https://zenn.dev/fukagawa/articles/9e0436fe7e6f25

NotionからObsidianに記事を移行する

エクスポート

特に説明するところはありませんが、Notionの記事から「マークダウンとCSV」を選んでファイルをエクスポートしておきます。

Obsidianの構造

以下のフォルダ構造に対応しています。00_Assetsは画像を格納するためのフォルダで、各記事のファイル名に基づいたフォルダごとに画像が保存されています。

MyVault/				
├── 00_Assets/                           				
│   ├── 2025-05-29T12-10-45/             				
│   │   ├── IMG-20250625010534873.jpg    				
│   │   ├── IMG-20250625010535037.jpg				
│   │   └── ...(他の画像)				
│   └── 2025-03-25T14-51-20/				
│       ├── IMG-20250625010149976.jpg				
│       └── ...(複数の画像)						
		
├── 2025-05-29T12-10-45.md   				
├── 2025-03-25T14-51-20.md    				
├── ...                        				

スクリプトの機能

  • .mdファイルのファイル名: 2025-03-25T14-51-20.md のような形式にリネーム
  • 画像フォルダ名: 対応する.mdと同じ名前にして 00_Assets/ の下へ移動
  • .mdファイル内の画像リンク: ![](00_Assets/対応フォルダ名/元の画像ファイル名) に書き換え
  • YAMLメタデータを記事の一番上に追加

できたスクリプト

コピペで使えます。画像の形式とかで合わないものに気づいたら更新していきます。
(ChatGPTで修正してます)

import os
import re
import shutil
from datetime import datetime
from urllib.parse import unquote

INPUT_DIR = "Notion_Export"
OUTPUT_DIR = "Obsidian_Vault"
ASSET_DIR = os.path.join(OUTPUT_DIR, "00_Assets")
os.makedirs(ASSET_DIR, exist_ok=True)

def clean_filename(name):
    name = unquote(name)
    name = name.replace(" ", "_").replace(" ", "_")
    return name

# ★ UUID削除
def normalize_name(name):
    # 末尾のUUIDっぽい部分を削除
    return re.sub(r"\s+[0-9a-f]{32}$", "", name)

# ★ フォルダ探索
def find_asset_folder(base_dir, md_filename):
    base_name = os.path.splitext(md_filename)[0]
    normalized_md = normalize_name(base_name)

    for item in os.listdir(base_dir):
        full_path = os.path.join(base_dir, item)

        if os.path.isdir(full_path):
            if item == normalized_md:
                return full_path

    return None

for file in os.listdir(INPUT_DIR):
    file_path = os.path.join(INPUT_DIR, file)

    if file.endswith(".md") and os.path.isfile(file_path):
        ts = datetime.fromtimestamp(os.path.getmtime(file_path))
        new_filename_base = ts.strftime("%Y-%m-%dT%H-%M-%S")

        new_md_path = os.path.join(OUTPUT_DIR, f"{new_filename_base}.md")
        new_asset_folder = os.path.join(ASSET_DIR, new_filename_base)

        old_asset_folder = find_asset_folder(INPUT_DIR, file)

        # === 画像コピー ===
        if old_asset_folder:
            os.makedirs(new_asset_folder, exist_ok=True)

            for img_name in os.listdir(old_asset_folder):
                old_img_path = os.path.join(old_asset_folder, img_name)

                if not os.path.isfile(old_img_path):
                    continue

                safe_name = clean_filename(img_name)
                new_img_path = os.path.join(new_asset_folder, safe_name)

                shutil.copy2(old_img_path, new_img_path)

            print(f"    └─ コピー成功: {old_asset_folder}")
        else:
            print(f"    └─ フォルダ未検出: {file}")

        # === md処理 ===
        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()

        def replace_link(match):
            path = match.group(1)
            filename = path.split("/")[-1]
            cleaned = clean_filename(filename)

            return f"![](00_Assets/{new_filename_base}/{cleaned})"

        content = re.sub(
            r'!\[[^\]]*\]\(([^)]+)\)',
            replace_link,
            content
        )

        yaml_header = """---
tags:
  - 
aliases:
  - 
UID: 
---

"""

        with open(new_md_path, "w", encoding="utf-8") as f:
            f.write(yaml_header + content)

        print(f"[✓] {file}")

print("\n=== 完了 ===")

スクリプトの実行方法

Notion_Exportというフォルダを作成し、その中に解凍したMarkdownファイルと画像ファイルを格納します。スクリプトも同じフォルダに配置します。

上記のフォルダの場所で、以下のコマンドでスクリプトを実行します。

python3 convert_notion_to_obsidian.py

Obsidian_Vaultというフォルダの中に、MDファイルと画像フォルダが生成されるので、それぞれをObsidianのVaultに移して完了です。

Notionのタグ情報とか、改行、空白行など手動で修正したほうが早いものは、そのままにしてあります。
(目視確認するし、Obsidian用に整形したいので、ある程度は手動がいいかなと思ってます)

URLリンクもメンションするなら手動で貼り直しです。今は動画にも対応していません。できたらまた、更新したいと思います。。。

Discussion