如何“狼狈”的将 Obsidian 文章发布到 CSDN?- Obsidian Publish To CSDN Tools 1.1

关注这个项目的最新进展:Obsidian Releases Tools Development Logs-CSDN博客

0x01:工具简介

Obsidian Publish To CSDN,是我开发的一款将 Obsidian 内容发布到 CSDN 的辅助工具,暂时还没有完成全自动化,有点呆呆的,但是也给我省了不少事。

Obsidian 笔记发布的时候,最令人头大的就是里面的双链图片了,因为 CSDN 他不认(我也懒得做图床啥的,本地笔记,如果和云上牵扯太大,感觉还不如用 “语雀”)。所以经常会出现,文字复制上去了,但图片还得一张一张的去 Obsidian 存放图片的位置,根据图片名称查找图片,然后再一张张上传到 CSDN 的情况,十分的麻烦,且不优雅。

那么,我开发的这个脚本,就是为了这种情况而生。

它可以快速提取出,Obsidian 笔记中的图片,并且顺带生成一份更适合 CSDN 发布的 Markdown 笔记。

0x02:工具使用

0x0201:工具初始化配置

复制脚本代码后,需要修改的内容仅一项,就是 OBSIDIAN_IMAGE_PATH,即你在 Obsidian 中设置的图片存储路径,这里的路径建议直接上绝对路径(以免你移动脚本,导致找不到图片),设置好后的结果如下:

后面的路径就是我 Obsidian 存放图片的位置:

0x0202:工具使用流程

1. 运行脚本

脚本的使用方法也很简单,直接运行就行,它会让你输入待发布的 Obsidian Markdown 文档路径:

这里我以今天发布的一个笔记为例,打开存放笔记的文件夹,选中待发布的笔记,然后右击,选择“复制笔记地址” 或者 Ctrl+Shift+C

将复制好的地址黏贴进脚本的提示框中,点击 Enter 即可运行脚本:

脚本运行成功后会在脚本同文件夹下生成一个 CSDN_PUBLISH_FOLDER 的文件夹,里面就是更方便发布的格式:

2. 发布至 CSDN

打开 CSDN 的文章发布页面,我使用的是富文本编辑器,可不是 Markdown 编辑器哦(虽然笔记是 Markdown 格式的)。

打开 00 - HackerBar - README.md 文档(就是脚本处理好后的你想发布的那个文档),可以看到原本 Obsidian 中的图片都被以序号编号好了(我是使用本地的 Typora 打开的 Markdown 文件,使用 Typora 复制文章到 CSDN 可以保留格式):

这个时候Ctrl + A 选中文章所有内容,然后Ctrl + C 复制到 CSDN 发布页面上,其结果如下,可以看到格式都保留的很好(这个得归功于 Typora,不然也可以使用 CSDN 的 Markdown 编辑模式):

但是图片还没贴上,这个时候,我们打开 images 文件夹,将图片依次拖到页面上即可(虽然还是比较麻烦,但比自己一个个搜索好多了):

0x03:工具源码

最后,附上工具源码(脚本中用到了shutil 包,可能需要自己下载一下,我本地运行的环境是 python 3.9.6)。

"""
-------------------------------------------------
   File Name:      Obsidian Publish To CSDN
   Description :    此脚本会提取出 Obsidian 笔记中的双链图片,生成一份可以直接复制进 CSDN 的笔记 + 笔记图片文件夹,
                    节省您搜索图片的时间
   Author :         Blue17
   Date:           2024/08/18
-------------------------------------------------
   Upgrading plan:

   Change Activity:
                    2024/08/19: 修复了当笔记中的代码块内同时出现 ``` 与双链图片格式时,会识别出错误的图片的问题。
-------------------------------------------------
"""

import re
import os
import shutil
# ============================================== Config
# Obsidian 存放图片资源的文件夹路径(绝对地址)
OBSIDIAN_IMAGE_FILE_PATH = r"G:\06 - ObsidianNotes\04 - Resource\0401 - Obsidian Notes Resource\ObsidianNotesAttachments"
# 适合 CSDN 体质的 Markdown 文件存储路径(到文件夹),默认为脚本同级文件夹中
RESULT_FOLDER = "CSDN_PUBLISH_FOLDER"
# ==============================================


def load_md_file(file_path):
    """
      导入 Obsidian Markdown 文件
      @file_path:Obsidian Markdown 文件路径
    """
    if os.path.exists(file_path):
        with open(file_path, "r", encoding="utf-8") as f:
            return f.readlines()
    else:
        print(f"[+] Error:{file_path} 文件不存在!")


def save_md_file(file_name, content):
    """
      保存 md 文件
      @param:file_name,保存的文件名
      @param: content,保存的文件结果
    """
    # 查看结果存放的文件夹是否存在,不存在就创建
    if os.path.exists(RESULT_FOLDER) == False:
        # 创建 RESULT_FOLDER 文件夹
        os.mkdir(RESULT_FOLDER)
        print(f"[+] 存放结果的文件夹 {RESULT_FOLDER} 创建成功!")

    # 保存 md 文件
    save_path = os.path.join(RESULT_FOLDER, file_name)
    with open(save_path, "w", encoding="utf-8") as f:
        f.write(content)
    print(f"[+] CSDN Markdown 文件保存成功,保存路径:{save_path}")


def copy_obsidian_image(image_path, image_save_folder=os.path.join(RESULT_FOLDER, "images")):
    """
      将 Obsidian 中的 IMAGE 复制到指定文件夹中
    """
    # 检查保存图片的文件夹是否创建好,没有则创建
    if os.path.exists(os.path.join(image_save_folder)) == False:
        os.mkdir(image_save_folder)
        print(f"[+] 存放图片的文件夹 {image_save_folder} 创建成功!")

    # 将图片复制到图片文件夹中
    for id, name in image_path.items():
        src_path = os.path.join(OBSIDIAN_IMAGE_FILE_PATH, name)
        dst_path = os.path.join(
            image_save_folder, f"IMAGE_{id:03d}."+name.split(".")[-1])
        try:
            shutil.copy(src_path, dst_path)
            print(f"[+] IMAGE_{id:03d} - 复制成功!")
        except Exception as e:
            print(e)


def main(obsidian_file_path):
    # 读取指定的 Obsidian Markdown 笔记内容
    file_content_lists = load_md_file(obsidian_file_path)

    # 一行一行的处理文档内容
    image_id = 1
    image_path = {}  # 存放图片路径
    line_no = 0
    while line_no < len(file_content_lists):
        # 检查当前行是否是代码块的起始位置
        code_block_sign = re.match(
            r"((>{0,} {0,}){0,})( {0,}`{3})", file_content_lists[line_no])   # 检查代码块 ``` 起始标志
        if code_block_sign != None:
            # 当前位置是代码块的起始位置 - 找到代码块的结束位置
            # 判断代码块是否在引用块中
            if '>' in code_block_sign.group():
                # 当前代码块处于引用块中,引用块中的代码块可以不设置结束标志,而直接以引用块的结尾作为结尾
                while line_no < len(file_content_lists):
                    line_no += 1
                    if ">" != file_content_lists[line_no][0]:   # 已经不在引用块内部了
                        break
                    new_sign = re.match(
                        r"((>{0,} {0,}){0,})( {0,}`{3})", file_content_lists[line_no])   # 检查代码块结束标志
                    if new_sign != None and new_sign.group() == code_block_sign.group():    # 如果匹配到了结束的符号
                        line_no += 1
                        break
            else:
                # 当前代码块不在引用块中
                while line_no < len(file_content_lists):
                    line_no += 1
                    # 检查普通代码块 ``` 起始标志
                    new_sign = re.match(
                        r"((>{0,} {0,}){0,})( {0,}`{3})", file_content_lists[line_no])
                    if new_sign != None and new_sign.group() == code_block_sign.group():    # 匹配了代码块结尾标识
                        line_no += 1
                        break
        else:
            # 当前位置不是代码块的位置,还需要防范 `` 行内代码块
            content = file_content_lists[line_no]   # 当前行文本
            content_split_result = re.split(
                r"(?<!\\)`", content)  # 负向后查找,匹配 ` 前不存在 \ 的
            # 分割后的内容:[内容1,代码块,内容2,代码块,内容3]
            for id, content in enumerate(content_split_result, start=1):
                if (id % 2 == 1):
                    # 提取文章中的图片
                    image_file_lists = re.findall(r"!\[\[(.*?)\]\]", content)
                    # 将图片保存到 image_path 中,并修改图片格式为 ![[IMAGE_ID]]
                    for image_file in image_file_lists:
                        # 修改图片格式为 ![[IMAGE_ID]]
                        content_split_result[id - 1] = content_split_result[id - 1].replace(
                            image_file, f"IMAGE_{image_id:03d}")
                        # 将图片名称保存到 image_path 中,| 是 Obsidian 中用来调整图片大小的,# 号是扩展语法
                        image_path[image_id] = os.path.basename(image_file).split("|")[0].split("#")[0]
                        image_id += 1
            # 将重构后的当前行文本拼接后重新赋值给当前行内容
            content = "`".join(content_split_result)
            file_content_lists[line_no] = content
            line_no += 1
    save_md_file(os.path.basename(obsidian_file_path),
                 "".join(file_content_lists))
    copy_obsidian_image(image_path)


if __name__ == "__main__":
    # 待发布的 Obsidian Markdown 文件路径
    obsidian_file_path = input("请输入待发布的 Obsidian Markdown 文件路径:").strip("\"")
    main(obsidian_file_path)

0x04:工具优化记录

0x0401:解决了 1.0 中的图片误识别问题

这里附上 Bug 触发样式(应该挺少见的):

这里附上 1.1 版本的识别结果(我把引用块中的代码块可以不设置结束符也考虑进去了):

Obsidian是一款强大的知识管理工具,让用户可以轻松地创建、组织和链接各种不同的想法、笔记和文档。与传统的笔记软件相比,Obsidian具有更强的结构化和跨文档链接的功能,使用户可以更好地构建自己的知识图谱。 为了与更多的人分享Obsidian的魅力和功能,我认为将其分享到CSDN是一个不错的选择。CSDN是一个面向中国开发人员的专业技术社区,拥有大量的IT从业者和技术爱好者。 首先,通过在CSDN发布Obsidian的相关文章或技术教程,我们可以吸引更多对知识管理和笔记软件感兴趣的读者。这些读者可能是学生、研究人员、写作人员、创业者等不同背景的人。他们可以通过学习Obsidian的使用方法,提高他们的知识组织和表达能力,从而更高效地学习、工作和创造。 其次,我们可以在CSDN上创建Obsidian的社群或论坛,与用户互动,分享使用心得和技巧。这样大家不仅可以相互学习和解决问题,还可以建立起一个Obsidian用户之间的交流网络,促进知识的共享和合作。这对于提高用户的满意度和粘性非常重要。 另外,我们还可以将Obsidian与其他技术或工具进行整合,如与Github、Typora等软件的配合使用。通过在CSDN上分享这些整合方法,我们可以帮助用户更好地利用Obsidian的强大功能和扩展性,以满足不同用户的需求。 总之,将Obsidian分享到CSDN可以让更多人了解和使用这款优秀的知识管理工具,提升个人和团队的工作效率和创造力。通过CSDN这个专业技术社区的平台,我们可以与更多开发者、技术爱好者和知识工作者进行互动和共享,推动Obsidian在国内的发展和应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SRC_BLUE_17

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值