当前位置: 首页 > news >正文

告别手动导出:一键将思源笔记自动同步到 Git 仓库

你是否也曾像我一样,在思源笔记中精心维护着一份指南或知识库,却在将其同步到 GitLab/GitHub 时感到烦恼?每次更新,都意味着一系列重复枯燥的操作:手动导出 -> 找到 Markdown 和图片 -> 复制粘贴到 Git 仓库 -> git add -> git commit -> git push

这个过程不仅耗时,而且极易出错。今天,我将与你分享如何通过一个 Python 脚本,将这整个流程彻底自动化,实现真正的“一键同步”,让你从此专注于内容创作。

最终目标

我们的目标是创建一个脚本,当它运行时,会自动完成以下所有事情:

  1. 调用思源笔记 API:自动请求思源笔记将指定文档(包含所有文本和图片)导出为一个 .zip 压缩包。
  2. 定位并移动文件:智能地找到思源笔记在本地生成的导出文件。
  3. 更新仓库:将新的 .zip 包移动到本地 Git 仓库,清空旧内容,然后解压新内容。
  4. 自动版本控制:自动执行 Git 命令,将所有变更提交并推送到远程仓库。

准备工作

在开始之前,请确保你已准备好以下环境:

  • 思源笔记桌面客户端:脚本运行时,它必须处于运行状态。
  • Python 3:下载并安装 Python。
  • Git:下载并安装 Git,并确保你的仓库已配置好远程推送权限。
  • 本地 Git 仓库:将你的目标 GitLab/GitHub 仓库克隆到本地。
  • 安装 requests :在终端中运行 pip install requests

步骤一:获取你的个人凭证

我们的脚本需要一些关键信息才能与你的思源笔记和仓库进行交互。

  1. 思源笔记 API Token

    • 打开思源笔记客户端,进入 设置 -> 关于 -> API Token
    • 点击“复制”按钮,这个 Token 就是脚本访问你笔记的钥匙。
  2. 文档 ID (Block ID)

    • 在笔记中找到你想导出的那篇文档。
    • 右键点击文档标题,选择 复制 -> 块链接
    • 你会得到类似 siyuan://blocks/20251029171326-62jlw08 的链接,其中 20251029171326-62jlw08 就是我们需要的文档 ID。
  3. 思源笔记临时导出目录

    • 这是最关键的一步!经过探索发现,思源笔记通过 API 导出时,并不是直接返回文件流,而是在本地的一个临时目录生成文件
    • 你需要找到这个目录。通常它位于你的思源笔记工作空间temp/export 文件夹下。在我的电脑上,这个路径是 D:\SiYuanDoc\temp\export。请根据你的实际情况找到并记录下这个路径。

步骤二:构建自动化脚本

现在,让我们把所有东西组合成一个强大的 Python 脚本。下面的代码整合了我们所有的发现,并包含了详细的注释和强大的错误处理机制。

将以下代码保存为 siyuan_sync.py

import requests
import os
import zipfile
import subprocess
import shutil
from datetime import datetime
import urllib.parse# --- 1. 请在这里配置你的个人信息 ---
SIYUAN_API_TOKEN = "在这里粘贴你的API Token"
SIYUAN_DOC_ID = "在这里粘贴你的文档ID"
GIT_REPO_PATH = r"D:\path\to\your\local\git\repo"  # 使用 r"" 原始字符串避免转义问题
SIYUAN_API_URL = "http://127.0.0.1:6806"
# 关键:你实际的思源笔记临时导出目录
SIYUAN_EXPORT_BASE_PATH = r"D:\SiYuanDoc\temp\export" # --- 2. 脚本核心功能 ---def export_from_siyuan():"""调用exportMd API,解析JSON响应,并从思源临时目录移动ZIP文件。"""export_url = f"{SIYUAN_API_URL}/api/export/exportMd"headers = {"Authorization": f"Token {SIYUAN_API_TOKEN}"}payload = {"id": SIYUAN_DOC_ID}print("正在向思源笔记请求导出...")try:response = requests.post(export_url, json=payload, headers=headers)response.raise_for_status()json_data = response.json()if json_data.get("code") != 0:print(f"错误:思源笔记API返回错误。")print(f"  错误码: {json_data.get('code')}")print(f"  错误消息: {json_data.get('msg')}")return None, Falserelative_zip_path = json_data["data"]["zip"]print(f"API返回相对路径: {relative_zip_path}")# 将URL编码的文件名解码成系统可识别的文件名global decoded_filenamedecoded_filename = urllib.parse.unquote(os.path.basename(relative_zip_path))full_source_zip_path = os.path.join(SIYUAN_EXPORT_BASE_PATH, decoded_filename)dest_zip_path = os.path.join(GIT_REPO_PATH, "siyuan_export.zip")if not os.path.exists(full_source_zip_path):print(f"错误:在思源导出目录中未找到文件: {full_source_zip_path}")print("请检查 SIYUAN_EXPORT_BASE_PATH 配置是否正确。")return None, Falseprint(f"正在从 '{full_source_zip_path}' 移动到 '{dest_zip_path}'...")shutil.move(full_source_zip_path, dest_zip_path)print(f"成功获取ZIP文件到Git仓库目录。")return dest_zip_path, Trueexcept requests.exceptions.RequestException as e:print(f"错误:无法连接到思源笔记。请确保思源笔记正在运行。详细信息: {e}")return None, Falseexcept KeyError:print(f"错误:API响应格式不正确。收到的响应: {response.text}")return None, Falsedef unzip_and_organize(zip_path):"""解压文件并整理到Git仓库根目录。"""temp_extract_path = os.path.join(GIT_REPO_PATH, "temp_extract")try:print("正在清理旧文件...")for item in os.listdir(GIT_REPO_PATH):if item in ['.git', os.path.basename(zip_path), 'temp_extract']:continueitem_path = os.path.join(GIT_REPO_PATH, item)if os.path.isdir(item_path):shutil.rmtree(item_path)else:os.remove(item_path)print("旧文件清理完毕。")print(f"正在解压 {zip_path}...")with zipfile.ZipFile(zip_path, 'r') as zip_ref:zip_ref.extractall(temp_extract_path)exported_content_path = ""unzipped_folder_name = os.path.splitext(decoded_filename)potential_path = os.path.join(temp_extract_path, unzipped_folder_name)if os.path.isdir(potential_path):exported_content_path = potential_pathelse:for item in os.listdir(temp_extract_path):path = os.path.join(temp_extract_path, item)if os.path.isdir(path): exported_content_path = path; breakif not exported_content_path:print("错误:未在ZIP包中找到预期的导出文件夹。"); return Falsefor item in os.listdir(exported_content_path):src_path = os.path.join(exported_content_path, item)dest_path = os.path.join(GIT_REPO_PATH, item)shutil.move(src_path, dest_path)print("文件解压和整理完成。"); return Trueexcept Exception as e:print(f"解压过程中发生错误: {e}"); return Falsefinally:if os.path.exists(zip_path): os.remove(zip_path)if os.path.exists(temp_extract_path): shutil.rmtree(temp_extract_path)def git_push():"""在Git仓库目录执行add, commit, push命令。"""print("正在执行Git操作...")try:base_cmd = ["git", f"--git-dir={os.path.join(GIT_REPO_PATH, '.git')}", f"--work-tree={GIT_REPO_PATH}"]status_result = subprocess.run(base_cmd + ["status", "--porcelain"], capture_output=True, text=True, check=True)if not status_result.stdout.strip():print("Git仓库内容无变化,无需提交。"); returnsubprocess.run(base_cmd + ["add", "."], check=True)commit_message = f"Automated update from Siyuan Note on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"subprocess.run(base_cmd + ["commit", "-m", commit_message], check=True)subprocess.run(base_cmd + ["push"], check=True)print("成功推送到GitLab仓库!")except Exception as e:print(f"Git操作失败: {e}")# 在全局作用域声明变量以供函数间共享
decoded_filename = ""if __name__ == "__main__":zip_file_path, export_ok = export_from_siyuan()if export_ok:unzip_ok = unzip_and_organize(zip_file_path)if unzip_ok:git_push()

步骤三:跑起来看看

  1. 填写配置:打开 siyuan_sync.py 文件,仔细填写顶部的四项个人配置信息。

  2. 确保思源在运行:这是必须的!

  3. 运行脚本:打开终端,进入脚本所在目录,执行命令:

    python siyuan_sync.py
    

更进一步:设置定时任务

  • 在 Windows 上:使用“任务计划程序”创建一个新任务,设置好触发时间和要执行的 python siyuan_sync.py 命令。
  • 在 macOS 或 Linux 上:使用 cron。在终端运行 crontab -e,添加类似 0 22 * * * /usr/bin/python3 /path/to/your/siyuan_sync.py 的行,即可实现每天晚上10点自动同步。
http://www.dtcms.com/a/577050.html

相关文章:

  • OPPO 后端校招面试,过于简单了!
  • element表格的行列动态合并
  • C++ 零基础入门与冒泡排序深度实现
  • 鸿蒙harmony将注册的数据包装成json发送到后端的细节及过程
  • JavaWeb(后端进阶)
  • VOC浓度快速测定仪在厂界预警中的实战应用:PID传感器技术与数据分析
  • 【SRE】安装Grafana实践
  • 在 PHP 中打印数据(调试、输出内容)
  • 网站运营有什么用做公司网站需要了解哪些东西
  • 段描述符属性测试
  • Ubuntu安装mysql5.7及常见错误问题
  • 第四届图像处理、计算机视觉与机器学习国际学术会议(ICICML 2025)
  • 网站后台编辑网站开发科普书
  • 单位加强网站建设专门做素菜的网站
  • Rust 在内存安全方面的设计方案的核心思想是“共享不可变,可变不共享”
  • NXP的GUI Guider开发LVGL
  • 《金仓KingbaseES vs 达梦DM:从迁移到运维的全维度TCO实测对比》
  • 【开题答辩全过程】以 基于Java的相机专卖网的设计与实现为例,包含答辩的问题和答案
  • 增量爬取策略:如何持续监控贝壳网最新成交数据
  • 400Hz 橡胶弹性体动刚度扫频试验系统指标
  • Weavefox 携手 GLM-4.6/4.5V 打造新一代智能厨房小助手
  • 如何建立网站后台wordpress 主题 翻译
  • 深入理解 Java 双亲委派机制:JVM 类加载体系全解析
  • Linux 进程间关系与守护进程
  • 基于 Cursor 的智能测试用例生成系统 - 项目介绍与实施指南
  • 时序数据库选型指南:从大数据视角切入,聚焦 Apache IoTDB
  • Node.js 环境变量配置实战:从入门到精通
  • 嵌入式系统入门指南
  • 一次丝滑的内网渗透拿下域控
  • 福建亨利建设集团有限公司网站互展科技网站建设