导出

语雀文档批量导出为Markdown文件 - Ken的杂谈(可用)
image.png

语雀文档批量导出为Markdown文件

一、前言

语雀的定位由之前的社区转向工具,也不提供批量导出Markdown的功能,有开发者提供了导出脚本可以通过语雀官方API帮我们把文档批量导出为Markdown,方便我们把文档导入notion等其他平台或者备份在本地,以备不时之需

环境依赖

工具 版本要求 本文环境
操作系统 macOS/Windows/Linux Windows 11
Python 3.x 3.11
pip from python 3.x pip 22.3.1 from python 3.11

二、操作步骤

1、创建Token

登录语雀后在账户设置中可以创建Token,供访问API使用:https://www.yuque.com/settings/tokens/new
根据最小授权原则,这里只授予读取知识库以及文档的权限即可
image.png

2、安装Python

访问官网,下载自己操作系统对应的Python3 https://www.python.org/downloads/
image.png
查看版本

  1. python -V
  2. #输出内容
  3. Python 3.11.0
  4. #如果输出的是Python 2.x,可以使用Python3 -v查看
  5. #后续依赖安装也要使用pip3命令

4、安装依赖

通过pip(Python内置包管理工具)安装代码所需依赖

  1. pip install requests psutil
  2. #输出内容
  3. Collecting requests
  4. Downloading requests-2.28.1-py3-none-any.whl (62 kB)
  5. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.8/62.8 kB 1.1 MB/s eta 0:00:00
  6. Collecting psutil
  7. Downloading psutil-5.9.4-cp36-abi3-win_amd64.whl (252 kB)
  8. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 252.5/252.5 kB 2.6 MB/s eta 0:00:00
  9. ###省略其他依赖安装内容###
  10. Installing collected packages: urllib3, psutil, idna, charset-normalizer, certifi, requests
  11. Successfully installed certifi-2022.9.24 charset-normalizer-2.1.1 idna-3.4 psutil-5.9.4 requests-2.28.1 urllib3-1.26.13

5、代码及配置

创建导出目录,例如:d:\yuque,~/yuque
1、创建配置文件config.json

  1. {
  2. "TOKEN": "your token",
  3. "USER_AGENT": "yuque_export",
  4. "BASE_URL": "https://customspace.yuque.com/api/v2",
  5. "DATA_PATH": "yuque_data"
  6. }
配置项 说明
TOKEN 替换为前面创建的Token
USER_AGENT 保持默认即可,无需调整
BASE_URL 语雀官方API地址,无误调整
DATA_PATH 文档导出目录,无需调整,会在导出目录中创建子目录

2、创建Python脚本yuque.py

  1. import json
  2. import sys
  3. import os
  4. import re
  5. import requests
  6. import psutil
  7. from datetime import datetime
  8. if getattr(sys, 'frozen', False):
  9. APPLICATION_PATH = os.path.dirname(sys.executable)
  10. else:
  11. APPLICATION_PATH = os.path.dirname('.')
  12. print(APPLICATION_PATH)
  13. jsonConfig = json.load(open(os.path.join(APPLICATION_PATH, "config.json"), encoding='utf-8'))
  14. class ExportYueQueDoc:
  15. def __init__(self):
  16. try:
  17. if getattr(sys, 'frozen', False):
  18. APPLICATION_PATH = os.path.dirname(sys.executable)
  19. else:
  20. APPLICATION_PATH = os.path.dirname('.')
  21. self.jsonConfig = json.load(open(os.path.join(APPLICATION_PATH, "config.json"), encoding='utf-8'))
  22. self.base_url = self.jsonConfig['BASE_URL']
  23. self.token = self.jsonConfig['TOKEN']
  24. self.headers = {
  25. "User-Agent": self.jsonConfig['USER_AGENT'],
  26. "X-Auth-Token": self.jsonConfig['TOKEN']
  27. }
  28. self.data_path = self.jsonConfig['DATA_PATH']
  29. except:
  30. raise ValueError("config.json 有误")
  31. def get_user_info(self):
  32. res_obj = requests.get(url=self.base_url + '/user', headers=self.headers)
  33. if res_obj.status_code != 200:
  34. raise ValueError("Token 信息错误")
  35. user_json = res_obj.json()
  36. self.login_id = user_json['data']['login']
  37. self.uid = user_json['data']['id']
  38. self.username = user_json['data']['name']
  39. print("=========== 用户信息初始化成功 ==========")
  40. def get_repos_data(self):
  41. repos_json = requests.get(self.base_url + '/users/' + self.login_id + '/repos', headers=self.headers).json()
  42. repos_list = []
  43. for item in repos_json['data']:
  44. rid = item['id'] # 知识库id
  45. name = item['name'] # 知识库名称
  46. repos_list.append({"rid": rid, "repos_name": name})
  47. return repos_list
  48. def get_article_data(self, repos_list):
  49. """获取文章数据"""
  50. article_list = []
  51. for repos in repos_list:
  52. article_datas = requests.get(self.base_url + '/repos/' + str(repos['rid']) + '/docs',
  53. headers=self.headers).json()
  54. for item in article_datas['data']:
  55. bid = repos['rid']
  56. title = item['title'] # 文章标题
  57. desc = item['description']
  58. slug = item['slug']
  59. article_list.append(
  60. {"bid": bid, "title": title, "desc": desc, "slug": slug, "repos_name": repos["repos_name"]})
  61. for item in article_list:
  62. per_article_data = requests.get(self.base_url + '/repos/' + str(item['bid']) + '/docs/' + item['slug'],
  63. headers=self.headers).json()
  64. posts_text = re.sub(r'\\n', "\n", per_article_data['data']['body'])
  65. result = re.sub(r'<a name="(.*)"></a>', "", posts_text)
  66. # all_datas.append({"title": item['title'], "content": result})
  67. yield result, item["repos_name"], item['title']
  68. def save_article(self, result, repos_name, title):
  69. current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  70. dir_path = f"{self.data_path}/{repos_name}"
  71. dir_path = dir_path.replace(".","_")
  72. filepath = dir_path + f"/{title}.md"
  73. dir_ret = os.path.exists(dir_path)
  74. if not dir_ret:
  75. os.makedirs(dir_path)
  76. #exists_ret = os.path.exists(filepath)
  77. try:
  78. with open(filepath, 'a', encoding="utf-8") as fp:
  79. fp.writelines(result)
  80. print(f"[{current_time}] {title} 导出完成")
  81. except Exception as e:
  82. print(f"[{current_time}] {title} 导出失败")
  83. def main(self):
  84. self.get_user_info()
  85. repos_list = self.get_repos_data()
  86. gen_obj = self.get_article_data(repos_list)
  87. for item in gen_obj:
  88. self.save_article(item[0], item[1], item[2])
  89. if __name__ == "__main__":
  90. yq = ExportYueQueDoc()
  91. yq.main()

6、执行导出

  1. python yuque.py
  2. #输出内容示例
  3. =========== 用户信息初始化成功 ==========
  4. [2022-12-06 22:56:55] 语雀批量导出Markdown-ken.io 写入完成
  5. [2022-12-06 22:56:55] 浅谈软件设计模式与设计原则-ken.io 写入完成
  6. [2022-12-06 22:56:55] 常用正则表达式收集-ken.io 写入完成
  7. [2022-12-06 22:56:55] XShell管理远程Linux服务器安装&配置教程-ken.io 写入完成
  8. [2022-12-06 22:56:55] WindowsNginx安装与配置教程-ken.io 写入完成
  9. [2022-12-06 22:56:55] Ken.io ASP.NET Core 3.1迁移到5.0

导出成功后可以在config.json设置的目录中看到导出结果
image.png

三、备注

1、可能碰到的问题

Python版本问题

  1. python yuque.py
  2. #输出内容
  3. File "yuque.py", line 78
  4. dir_path = f"{self.data_path}/{repos_name}"
  5. ^
  6. SyntaxError: invalid syntax

这种情况可能是因为系统安装了多个Python版本,而默认的Python版本是2.x
这时候我们可以用以下命令指定Python3

  1. #安装依赖
  2. pip3 install requests psutil
  3. #执行导出
  4. python3 yuque.py

2、本文参考

https://blog.csdn.net/weixin_44797182/article/details/128059847