Typora 笔记迁移 Obsidian 图片链接转换

附件:Typora 笔记迁移 Obsidian 图片链接转换

需求

将typora笔记库迁移到Obsidian,但是typora的图片使用的是严格markdown ![](),Obsidian是wiki ![[]]

笔记库文件夹下面有超级多的.md笔记,手动操作太慢了,因此采用Python进行处理,进行批量修改文章链接格式。

![Pasted image 20221223164738.png](./Database/Pasted image 20221223164738.png)---->![[Pasted image 20221223164738.png]]

由于Obsidian是wiki ![[]] 因此后面采用程序或手动方式,将所有的图片移动到Obsidian仓库下指定图片文件夹即可,例如asset

思路

1、检测该链接是否为图片?![](./Database/
2、正则表达式匹配,![](./Database/任意内容)
3、替换`![](./Database/任意内容)`为`![[任意内容]`---->`![[Pasted image 20221223164738.png]`

实践

备份整个目标文件夹,打压缩拷贝该文件夹副本进行操作。

参考目录

|-- 转换
|   |-- typora_to_obsidian.py
|   `-- 综合软件使用教程 # 目标文件夹
|       |-- Database

安装python,网上教程很多,不再赘述。

  1. 保存脚本: 将下面的代码保存为一个 .py 文件,例如 rename_md.py

  2. 运行脚本: 打开终端或命令行工具,进入你保存脚本的目录,然后运行它:

    typora_to_obsidian.py
    

    程序会提示您输入笔记库的文件夹路径。只需将文件夹的绝对路径复制粘贴进去,然后按回车键即可。
    image

  3. 检查结果: 脚本会自动扫描该文件夹及其所有子文件夹中的 .md 文件,并完成链接的替换。处理过程会显示在终端中。

检查

谨慎一下,要进行检查,打开修改后的任意.md笔记,在typora中进行正则表达式查找:
!\[([^\[\]]*)\]\(([^()]*)(\s+["'].*?["'])?\) 可见,下面没有被替换的均为网络链接格式和示范笔记。

手动将转换成功的.md笔记移动到Obsidian笔记库中的新的位置。
采用程序或手动方式,将所有的图片移动到Obsidian仓库下指定图片文件夹即可,例如asset
打开一篇笔记,可见正常显示。

image

因此,本次转换图片链接操作成功!

image

代码

Python代码

import os
import re
import sys

def convert_image_links(directory):
    """
    遍历指定目录下的所有 Markdown 文件,并将 Typora 格式的图片链接转换为 Obsidian 格式。

    Typora 格式 1: ![](./some_folder/image.png)
    Typora 格式 2: ![alt-text](./some_folder/image.png)
    Obsidian 格式: ![[image.png]]
    """
    # 支持常见的图片文件扩展名
    image_extensions = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg', 'webp']
    # 构建正则表达式,用于匹配 Typora 的图片链接
    # 解释:
    # !\[.*?\]\(   : 匹配 "![",然后是任意数量的字符 (alt text),然后是 "]("
    # (?:.*/)?    : 匹配并忽略路径部分 (e.g., "./Database/")。
    #              (?:...) 是一个非捕获组。
    #              .* 表示任意字符,/ 是路径分隔符。? 表示路径是可选的。
    # (.*?)        : 捕获图片文件名 (非贪婪模式)。这是我们需要的核心内容。
    # \.           : 匹配文件名和扩展名之间的点。
    # ({})         : 将支持的图片扩展名插入正则表达式。
    # \)           : 匹配最后的 ")"
    pattern_str = r'!\[.*?\]\((?:.*/)?(.*?\.({}))\)'.format('|'.join(image_extensions))
    # 添加 re.IGNORECASE 标志以忽略扩展名的大小写
    image_pattern = re.compile(pattern_str, re.IGNORECASE)

    # 遍历指定目录
    for root, _, files in os.walk(directory):
        for filename in files:
            if filename.endswith('.md'):
                file_path = os.path.join(root, filename)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()

                    # 使用正则表达式查找并替换所有匹配的链接
                    # sub 函数的第二个参数 r'![[\1]]' 中的 \1 代表正则表达式中第一个捕获组的内容,
                    # 也就是我们需要的图片文件名 (e.g., "image-20250726104501340.png")
                    new_content, num_replacements = image_pattern.subn(r'![[\1]]', content)

                    if num_replacements > 0:
                        # 如果发生了替换,则将新内容写回文件
                        with open(file_path, 'w', encoding='utf-8') as f:
                            f.write(new_content)
                        print(f"处理文件: {file_path}")
                        print(f"  -> 成功转换 {num_replacements} 个链接。")

                except Exception as e:
                    print(f"处理文件 {file_path} 时出错: {e}", file=sys.stderr)

    print("\n所有文件处理完毕!")

if __name__ == '__main__':
    # 提示用户输入要处理的文件夹路径
    # 在 Windows 上,你可以直接复制文件夹路径,例如: C:\Users\YourName\Documents\MyNotes
    # 在 macOS 或 Linux 上,路径类似: /Users/YourName/Documents/MyNotes
    target_directory = input("请输入您的笔记库文件夹的绝对路径: ")

    if os.path.isdir(target_directory):
        convert_image_links(target_directory)
    else:
        print("错误: 您输入的不是一个有效的文件夹路径。", file=sys.stderr)


正则表达式代码

!\[([^\[\]]*)\]\(([^()]*)(\s+["'].*?["'])?\)

这个正则表达式 !\[([^\[\]]*)\]\(([^()]*)(\s+["'].*?["'])?\) 是用来精确匹配并提取标准 Markdown 格式的图片链接的各个部分的。

整体结构

这个表达式匹配的完整格式是: ![alt text](image_path "optional title")

它被分成了几个主要部分来分别捕获 alt text (替代文本)、 image_path (图片路径) 和 optional title (可选的标题)。

各部分详解

正则表达式部分 含义解释
! 匹配一个字面量的感叹号 !,这是 Markdown 图片语法的起始标志。
\[ 匹配一个字面量的左方括号 [。因为 [ 是元字符,所以需要用 \ 来转义。
([^\[\]]*) 第一个捕获组 (Group 1): • (...):定义一个捕获组,用于提取括号内的匹配内容。 • [...]:定义一个字符集。 • ^:在字符集 [] 内部表示“非”,即匹配任何不包含在后面的字符。 • \[\]:这里指的是 [] 这两个字符。 • *:表示匹配前面的字符集零次或多次。 作用:捕获方括号 [] 之间的所有内容,即图片的 替代文本 (alt text)
\] 匹配一个字面量的右方括号 ]
\( 匹配一个字面量的左圆括号 (
([^()]*) 第二个捕获组 (Group 2): • (...):定义捕获组。 • [^()]:匹配任何不是 (不是 ) 的字符。 • *:匹配零次或多次。 作用:捕获圆括号 () 之间的内容,即图片的 路径或URL (image_path)
(\s+["'].*?["'])? 第三个捕获组 (Group 3),且为可选部分: • (...):定义捕获组。 • ?:表示整个第三个捕获组是可选的(可以出现0次或1次)。 • \s+:匹配一个或多个空白字符(如空格)。 • ["']:匹配一个双引号 " 或一个单引号 '。 • .*?:非贪婪匹配。匹配任意字符 (.) 零次或多次 (*),但尽可能少地匹配 (?),直到遇到下一个模式。 • ["']:匹配一个结束的双引号 " 或单引号 '作用:捕获可选的图片标题 (title),它由空格和引号包围。
\) 匹配一个字面量的右圆括号 )

总结

  • !\[ ... \]: 匹配 ![alt text] 的框架。
  • (...): 括号内的部分是捕获组,意味着在查找替换时,你可以用 $1, $2, $3 (或 \1, \2, \3,取决于具体工具) 来引用这些捕获到的内容。
    • $1 对应 ([^\[\]]*),即图片的替代文本。
    • $2 对应 ([^()]*),即图片的路径。
    • $3 对应 (\s+["'].*?["'])?,即可选的、带引号的标题。

这个正则表达式可以准确地处理包含或不包含可选标题的各类标准 Markdown 图片链接,同时避免错误匹配嵌套的括号或格式不规范的文本。

注意事项

  1. 备份: 强烈建议在运行此脚本前,先备份你的整个文件夹。 文件重命名是一个不可逆的操作,以防万一出现非预期的结果。

参考

  1. [wiki链接和markdown链接 对比 互相转换](https://garden.czchx.cc/czc知识库/笔记/知识库搭建/1-obsidian笔记/子/wiki链接和markdown链接 对比 互相转换/)
  2. PKMer_Wiki 链接和 markdown 链接之间的转换
  3. 一文搞懂正则表达式 - 知乎
posted @ 2025-10-01 14:01  舟清颺  阅读(75)  评论(0)    收藏  举报