背景

最初,用 Hexo + GitHub 搭建了自己的博客,图片也托管在 GitHub 图床上。
可惜大陆访问不太友好,加载常常慢得令人抓狂。于是改用 OSS,想着图个省心。
结果没过多久,短信提醒接二连三地催我充值,倒成了新的烦恼。
索性停掉博客,转向安静的 语雀。
后来发现,语雀的文档不仅可以导出为Markdown,而且“白嫖”的专业会员也能让 知识库公开访问。
于是,这篇文章就这样诞生了。

导出文档

一开始,我打算用语雀的官方 API 对接 Hexo,这样文章就能自动同步更新。
但现实是——API 需要Token,而这个 Token 只有超级会员才能生成。
我这“白嫖”的专业会员显然不在待遇范围内。
没关系,既然不给接口,那我就自己爬。

访问公开知识库 https://www.yuque.com/076w/syst1m 遍历文章、目录等信息,通过/api/docs/{slug}?book_id={book_id}&mode=markdown取到文档的 Markdown 源码导出即可。

这里本来套的蹭一些公开的 cdn 做一个图片中转来着 但是!!

百度 cdn 不给我蹭了 直接抄作业了 下面的导出可以正常使用,已去除图片转换

Hexo 使用语雀图床防盗链最终解决方案 ——hexo-yuque-picture

转换文档

为了让导出的 Markdown 能直接发布,在导出时就顺便按照 Hexo 的格式做了调整,参考了 Hexo Front-matter 文档

  • title:文章标题
  • categories:父目录链路,例如 笔记/靶场日记/HackThebox
  • tags:取最后一级目录名作为一个 tag,例如 HackThebox
  • id:文档 ID
  • date:创建时间
  • updated:更新时间

本来打算把文章封面图片设为文章中的第一张图,但效果太丑,最终没有采纳。

操作示例

假设你的语雀知识库链接是:https://www.yuque.com/076w/syst1m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import sys
import requests
import json
import re
import os
import urllib.parse
from datetime import datetime

def save_page(book_id, slug):
"""下载文档内容(不替换图片链接)"""
url = f'https://www.yuque.com/api/docs/{slug}?book_id={book_id}&merge_dynamic_data=false&mode=markdown'
r = requests.get(url)
if r.status_code != 200:
print("文档下载失败:", slug, r.content)
return None, None, None
data = r.json().get('data', {})
content = data.get('sourcecode', '')

# 不再替换图片链接,保留原始链接

created_at = data.get('created_at')
published_at = data.get('published_at')
updated_at = data.get('updated_at') or published_at or created_at
return content, created_at, updated_at


def build_categories(doc, list_dict):
"""根据父子关系构建 categories 数组"""
categories = []
parent = doc.get('parent_uuid')
while parent:
parent_doc = list_dict.get(parent)
if not parent_doc:
break
categories.insert(0, parent_doc.get('title', ''))
parent = parent_doc.get('parent_uuid') or None
return categories


def safe_datetime_str(iso_ts):
"""格式化 ISO 时间"""
if not iso_ts:
return ''
try:
if iso_ts.endswith('Z'):
iso_ts = iso_ts[:-1]
dt = datetime.fromisoformat(iso_ts)
return dt.strftime('%Y-%m-%d %H:%M:%S')
except Exception:
return iso_ts


def get_book(url="https://www.yuque.com/076w/syst1m"):
r = requests.get(url)
m = re.findall(r"decodeURIComponent\(\"(.+)\"\)\);", r.text)
if not m:
print("未在页面中找到 window.appData 数据,请检查 URL")
return
docsjson = json.loads(urllib.parse.unquote(m[0]))

book_id = str(docsjson['book']['id'])
book_dir = f"download/{book_id}"
os.makedirs(book_dir, exist_ok=True)

list_dict = {doc['uuid']: doc for doc in docsjson['book']['toc']}

for doc in docsjson['book']['toc']:
if doc.get('type') == 'DOC' and doc.get('url'):
content, created_at, updated_at = save_page(book_id, doc['url'])
if content is None:
continue

categories = build_categories(doc, list_dict)

fm_lines = ["---", f"title: {doc.get('title', '')}"]

# 如果有上级目录,则写 categories 和 tags
if categories:
fm_lines.append(f"categories: {json.dumps(categories, ensure_ascii=False)}")
fm_lines.append(f"tags: {json.dumps([categories[-1]], ensure_ascii=False)}")

fm_lines.append(f"id: {doc.get('doc_id', '')}")
fm_lines.append(f"date: {safe_datetime_str(created_at)}")
fm_lines.append(f"updated: {safe_datetime_str(updated_at)}")
fm_lines.append("---")
fm = "\n".join(fm_lines) + "\n\n"

safe_title = re.sub(r'[\\/*?:"<>|]', '_', doc.get('title', 'Untitled')).strip()
md_file = os.path.join(book_dir, safe_title + '.md')

with open(md_file, 'w', encoding='utf-8') as f:
f.write(fm + content)

print(f"已保存: {md_file}")


if __name__ == '__main__':
if len(sys.argv) > 1:
get_book(sys.argv[1])
else:
get_book()

1、 保存脚本
将上面的 Python 脚本保存为 yuque_export.py

2、 安装依赖

pip install requests

3、 执行脚本

python yuque_export.py https://www.yuque.com/076w/syst1m

4、 生成 Markdown 文件
脚本会在当前目录下生成 download/<book_id>/ 文件夹,每篇文档对应一个 .md 文件,Front-matter 已按 Hexo 格式生成,图片链接也已替换为可访问的中转链接。

5、 发布到 Hexo
.md 文件放入 Hexo 的 source/_posts/ 文件夹,即可直接发布。


本站总访问量

本站由 076w 使用 Stellar 1.33.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。