因为作者有点菜菜的,直到发布本篇文章前,作者才发现本次脚本中还有部分 Bug 未修(但对普通用户其实也挺难触发的),所以,就不写优雅了。
优雅等到我弄个全自动化的时候,我就改名叫“优雅”。
0x01:问题描述
我是今年入坑的 Obsidian,同时也是今年准备运营一下我的 CSDN 账号,但是如何将 Obsidian 中的笔记快速的发布到 CSDN 中,成了难倒我的一件事(记得上次在 CSDN 写文章我就是因为太麻烦了,所以转到“语雀”上去了)。
就在刚刚,我将一篇 Obsidian 的笔记转到 CSDN 上了,但是过程实在是痛苦,特别是双链图片,在 Obsidian 中,图片是以下面的格式存放的:
这个格式可不能直接复制到 CSDN 上发布,而且 Obsidian 还没有直接复制图片的功能,所以,没办法,我只能去存放图片的文件夹中一个个搜索图片,然后再一张张贴到 CSDN 上。
我觉得搜图片,再贴很麻烦,所以,我推出了我的 Obsidian CSDN 发布工具 1.0(节省了我们找图片的时间,我是想弄个自动化工具的,but,我是小菜鸡,后续慢慢补)
0x02:脚本使用
找图片很烦,所以我直接写了一个脚本,提取出 Obsidian 文章中的图片,将图片移动到一个文件夹中并标好序号,是不是就能节省不少时间了。
下面简单介绍一下,我的 ObsidianPublishToCSDN.py 脚本的使用流程。
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:脚本源码
最后,附上我写的 ObsidianPublishToCSDN.py 的 1.0 版本,后续我会争取搞个纯自动化的,写上路径就自己全部弄好(脚本中用到了 shutil 包,可能需要自己下载一下)。
"""
-------------------------------------------------
File Name: Obsidian Publish To CSDN
Description : 此脚本会提取出 Obsidian 笔记中的双链图片,生成一份可以直接复制进 CSDN 的笔记 + 笔记图片文件夹,
节省您搜索图片的时间
Author : Blue17
Date: 2024/08/18
-------------------------------------------------
Upgrading plan:自动化将选中的 Obsidian 添加到
Change Activity:
2024/08/18:
-------------------------------------------------
"""
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.read()
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 extract_image_file(file_content):
"""
提取出文件中所有的图片名称(不包含在代码块中的),并修改图片格式为 ![[IMAGE_ID]]
"""
# 按照 ``` 分割文档内容(两个 ``` 内的是代码块,该部分内容(丢弃)) [1,代码块,2,代码块,3]
filtration_01_result_list = file_content.split("```")
image_id = 1
image_path = {} # {IMAGE_ID:"IMAGE_PATH"}
new_content = ""
# 代码块中还有可能存在小代码块,以 ` 分割小代码块 [1,小代码块,2,小代码块]
for id, block in enumerate(filtration_01_result_list, start=1):
if (id % 2 == 1):
# 将代码块分成小代码块
filtration_02_result_list = block.split('`')
# 处理每个小块,代码块保留,文章提取内容,并替换图片格式
for id, content in enumerate(filtration_02_result_list, 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 = content.replace(
image_file, f"IMAGE_{image_id:03d}")
# 将图片名称保存到 image_path 中
image_path[image_id] = os.path.basename(image_file)
image_id += 1
new_content += content
else:
new_content += "`" + content + "`"
else:
# 如果是代码块则直接添加进新文档
new_content += "```" + block + "```"
# 返回新文件内容和图片路径
return new_content, image_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])
shutil.copy(src_path, dst_path)
print(f"[+] IMAGE_{id:03d} - 复制成功!")
if __name__ == "__main__":
# 待发布的 Obsidian Markdown 文件路径
obsidian_file_path = input("请输入待发布的 Obsidian Markdown 文件路径:").strip("\"")
file_content = load_md_file(obsidian_file_path)
new_content, image_path = extract_image_file(file_content)
save_md_file(os.path.basename(obsidian_file_path), new_content)
copy_obsidian_image(image_path)
0x04:脚本 Bug 记录
[!bug] 代码块内部出现图片双链格式的错误解析 当在 Markdown 的代码块内部出现了 Obsidian 双链图片格式,脚本可能无法识别出准确的图片。原因,是作者比较菜,还没想好怎么写。(作者已经想好了,明天修 BUG 然后再水一篇) 作者写的代码是全文读取然后匹配的,Markdown 肯定是按行匹配的。