ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Beancount 文档自动化流水线解析:Google Docs 到 RST 的下载、转换与发布工具链

Beancount 文档自动化流水线解析:Google Docs 到 RST 的下载、转换与发布工具链 Beancount 文档自动化流水线解析Google Docs 到 RST 的下载、转换与发布工具链【免费下载链接】beancountBeancount: Double-Entry Accounting from Text Files.项目地址: https://gitcode.com/GitHub_Trending/be/beancount本文围绕 Beancount 仓库中experiments/docs_rst目录下的文档转换工具集系统讲解其「下载Download→ 转换Convert→ 拷贝Copy」三步自动化流水线。该流水线服务于 Beancount 的文档发布流程文档的原稿保存在 Google Docs 中每次发布前通过脚本自动拉取、转换为 reStructuredTextRST并交付给静态站点生成器。读完本文你将掌握这套工具链的完整架构、每个脚本的核心实现原理、Makefile的编排方式以及如何基于仓库源码复现和扩展这套文档发布方案。一、背景为什么需要一个文档转换流水线Beancount 是一个基于文本文件的复式记账系统其官方文档长期以 Google Docs 为唯一事实来源pristine source。这种协作方式便于多人共同编辑但 Google Docs 的原生格式无法直接用于静态网站发布。为此仓库在 experiments/docs_rst 目录下维护了一套实验性工具链目标是在每次发布新版本之前自动化完成三件事下载从 Google Drive 拉取最新版本文档转换将下载的.docx等格式转换为 reStructuredTextRST拷贝把转换产物拷贝到静态站点生成器的源目录随后用 Sphinx 构建并发布。README 明确说明这套工具的目标是「自动下载并转换所有 Google Docs 文档为 Markdown/RST转换结果最终集成进 Beancount 本体」而 Google Docs 始终保持为源文档转换应在每次发布前自动执行。二、三步流水线总览Makefile 的编排逻辑整个流水线由 Makefile 统一编排定义了三个核心 target 和两个关键配置变量。配置变量变量默认值作用CONVERT_DOCS$(HOME)/docs下载文件的存放目录既可以是 Google Drive API 下载结果也可以是解压后的归档文件RST_DOCS$(HOME)/p/beancount-docsDominik Aumayr 的静态文档生成器源码目录转换产物拷贝的目标位置三个 Make target# Download all the docs. download: mkdir -p $(CONVERT_DOCS) ./download_docs.py $(CONVERT_DOCS) # Convert all the docs. convert: ./convert_docs.py $(CONVERT_DOCS) # Copy the docs to the static archive. # (You can then go and build then using Sphinx and inspect them.) copy: ./copy_docs.py $(CONVERT_DOCS) $(RST_DOCS)对应命令行的完整用法为make download # 从 Google Drive 下载文档 make convert # 将下载的文档转换为 RST make copy # 将转换产物拷贝到静态站点仓库三个命令须按顺序执行且make convert依赖make download的产物。make copy完成后即可进入RST_DOCS目录用 Sphinx 构建文档并检查效果——这也是 Makefile 注释中提到的后续步骤。三、下载阶段download_docs.py的实现细节download_docs.py 负责连接 Google Drive API 下载文档是流水线的数据入口。3.1 多格式下载策略脚本针对每一篇文档尝试下载7 种格式以评估哪种格式最适合作为转换源扩展名MIME 类型Pandoc 输入格式htmltext/htmlhtmltxttext/plain无rtfapplication/rtf无odtapplication/vnd.oasis.opendocument.textodtpdfapplication/pdf无docxapplication/vnd.openxmlformats-officedocument.wordprocessingml.documentdocxepubapplication/epubzipepub这一定义位于源码FORMATS列表中download_docs.py。下载时通过files.export(fileIddocid, mimeTypemime_type)调用 Drive v3 API 导出对应格式文件按输出目录/文档ID/清洗后文件名.扩展名的目录结构落盘。3.2 文档 ID 的自动发现机制脚本内置了一个索引文档 IDINDEX_DOCID 1RaondTJCS_IUPBHFNdT8oqFKJjVJDsfsn6JEjBG04eA通过get_docids_from_index()下载该索引页的 HTML用 BeautifulSoup 解析其中所有指向document/d/ID/模式的链接从而自动枚举全部相关文档match re.search(rdocument/d/(.*)/, href) if not match: continue yield match.group(1)同时支持--docid参数手动指定单篇文档跳过索引发现流程。3.3 认证与命令行参数认证使用 Google 服务账号service account凭据文件默认位于$HOME/.google-apis-service-account.json申请drive与documents.readonly两个 scope源码见 get_auth_via_service_account。注意源码注释提示oauth2client已废弃。命令行参数./download_docs.py [--docid 文档ID] [-J|--download-jsons] [-C|--download-conversions] output--docid只下载指定文档-J/--download-jsons额外通过 Docs v1 API 下载文档内部 JSON 表示含结构化元素信息-C/--download-conversions下载全部 7 种格式并对 Pandoc 支持的格式html/odt/docx/epub额外调用pandoc --fromfmt --tonative生成 Pandoc 原生 AST 表示用于对比各格式的结构保真度output必选的位置参数输出目录。下载时若目标文件已存在且非空会打印File already present ... skipping并跳过保证可重复执行。四、格式选型依据notes-about-formats.txt的实验结论notes-about-formats.txt 记录了作者对不同导出格式质量的对比分析是整套工具设计的重要依据可下载的格式html、txt、rtf、odt、pdf、docx、epubPandoc 可读取的格式html、odt、docx、epub对比结论HTML 与 EPUB 的导出包含大量样式元素表示质量较差ODT 解析出的结构信息有限连章节标题都未被识别为标题而是包含锚点标记DOCX 是最有用的格式但存在三个已知问题不生成标题可能需要同时下载多种格式以从不同格式提取不同部分、包含多余的 blockquote、代码块未被识别且账户名前的空白会丢失。正是这些分析直接催生了convert_docs.py中「直接解析 docx XML 提取代码块」的特殊逻辑。五、转换阶段convert_docs.py的核心算法convert_docs.py 是流水线中技术含量最高的部分其核心思路是不信任 Pandoc 对代码块的默认处理而是直接从.docx包内部提取等宽字体段落再用匹配算法回填到 Pandoc 转换结果中。5.1 主流程ConvertDocx()GetDocxBlocks(filename) ──► 从 word/document.xml 提取等宽字体块带 RST 转义 │ PandocDocxToRst(filename) ──► pandoc -f docx -t rst 生成基线 RST 文本 │ GetRstBlocks(lines_txt) ──► 从 RST 中定位 blockquote 块 | 前缀 │ ComputeKey() 匹配 docx 块与 rst 块 │ 将 docx 提取的块按行号回填替换 rst 中的对应块5.2 提取 docx 中的代码块GetDocxBlocks()docx本质上是 ZIP 压缩包正文位于word/document.xml。脚本用zipfile.ZipFile打开文件用 BeautifulSoup 解析 XML找出所有使用了Consolas 等宽字体w:rfonts属性w:cs为Consolas的段落w:pfor wp in soup.find_all(w:p): if not wp.find(w:rfonts, attrs{w:cs: Consolas}): continue随后遍历段内每个w:r元素处理w:t文本与w:br换行节点并根据w:b/w:i属性标注加粗与斜体。提取后还有一道过滤单行且长度超过 80 字符的片段被判定为「不太可能是引用块」而丢弃。由于最终目标是 RST文本转义通过ConvertToRst()完成——对*做转义加粗转**text**、斜体转*text*脚本中还保留了一个ConvertToMarkdown()变体转义* _ 说明该工具链早期版本同样面向 Markdown 输出。5.3 Pandoc 转换与 RST 块定位PandocDocxToRst()与GetRstBlocks()基线转换直接调用系统 Pandocsubprocess.check_output([pandoc, -f, docx, -t, rst, filename], ...)GetRstBlocks()负责从 Pandoc 产物中定位 blockquote。它先做预处理将「空行 4 空格缩进行 空行」的单行块统一改写为|前缀形式然后扫描所有|前缀行及其续行 6 空格缩进聚合为块记录每个块的起止行号。5.4 块匹配与回填ComputeKey()与替换逻辑ComputeKey()将文本块规约为可比对的 key去除省略号…、...、压缩空白、递归剥离**与*标记。docx 提取块与 RST 文本块分别建 key 字典后逐一匹配匹配成功的块用 docx 中保留原始缩进与结构的文本按行号替换Pandoc 输出中对应区间del lines_txt[minline : maxline 1] new_lines [ | {}.format(line) for line in block_docx] lines_txt[minline:minline] new_lines替换时用offset变量补偿行数差保证后续替换的行号仍然准确。这正是 notes 中「代码块未被识别、账户名前的空白丢失」问题的针对性修复用等宽字体块的真实文本覆盖 Pandoc 丢失缩进的文本。5.5 批量入口main()通过FindDocxFiles()递归扫描目录下所有.docx文件也接受单个.docx文件路径逐文件转换输出为同名.rst文件。六、拷贝阶段copy_docs.py的目标映射copy_docs.py 负责把转换产物放置到静态站点仓库的正确位置。其核心是一份手工维护的FILE_PAIRS映射表源码见 copy_docs.py将「Google Docs ID文件夹式哈希/RST 文件名」映射到目标站点中的文档路径源转换产物目标路径.../Beancount-Motivation.rstusers/cl_accounting.rst.../Beancount-Install.rstusers/installation.rst.../Beancount-The_Double-Entry_Counting_Method.rstusers/double_entry_method.rst.../Beancount-Running_Reports.rstusers/running_and_reports.rst.../Beancount-Getting_Started.rstusers/getting_started.rst.../Beancount-Language_Syntax.rstusers/language.rst.../Beancount-Precision_Tolerances.rstusers/precision_tolerances.rst.../Beancount-Query_Language.rstusers/bql.rst.../Beancount-Syntax_Cheat_Sheet.rstusers/cheat_sheet.rst.../Beancount-How_Inventories_Work.rstusers/inventories.rst.../Beancount-Exporting_your_Portfolio_New_.rstusers/exporting.rst.../Beancount-Tutorial_Example.rstusers/tutorial.rst.../Beancount-Price_in_Beancount.rstusers/fetching_prices.rst.../Beancount-Cookbook.rstcookbook/cl_cookbook.rst.../Beancount-Trading_with_Beancount.rstcookbook/trading.rst.../Beancount-Cookbook-Vesting.rstcookbook/stock_vesting.rst.../Beancount-Cookbook-Sharing_Expenses.rstcookbook/sharing_expenses.rst.../Beancount-Scripting_Plugins.rstdevelopers/scripting_plugins.rst.../LedgerHub-Design_Doc.rstdevelopers/design.rst.../Beancount-Contributions.rstdevelopers/external_contributions.rst可见目标站点按users/用户指南、cookbook/实战手册、developers/开发者文档三大分区组织。另有若干文档如 Importing External Data、Comparison/Differences、多项 Proposal、Design Doc、History and Credits 等在源码中以注释形式列出但未加入拷贝映射。执行时脚本接收两个位置参数——转换文档根目录与目标站点根目录遍历映射执行shutil.copyfile./copy_docs.py 转换文档根目录 静态站点根目录七、与当前仓库文档体系的关联本工具链服务于 Beancount 的文档发布生态与仓库当前的文档基础设施存在清晰对应转换产物的目标结构users/、cookbook/、developers/分区与当前仓库内 docs/site_docs 目录包含 index.md以及 experiments/docs 中的文档组织思路一脉相承当前静态站点构建仓库根目录的 docs/Makefile 展示了新一代构建方式——通过uv sync安装依赖、uv run zensical build -f zensical.yml使用 zensical 静态站点生成器构建、uv run zensical serve本地预览。相比experiments/docs_rst中「转换到外部beancount-docs仓库再用 Sphinx 构建」的旧方案可见文档工具链本身也在持续演进旧版工具的历史脉络experiments/docs_rst/old 目录保留了更早期的实现docs.py共享工具库、convert_doc.py单文档转换、convert_filter_docx.pyPandoc 过滤器、docs_test.py单元测试其思路与现版本一脉相承——同样基于 Google Drive API、索引页发现、多格式下载与 Pandoc 处理。八、运行前提与注意事项Python 依赖脚本依赖apiclientGoogle API Client、httplib2、oauth2client、bs4BeautifulSoup等第三方库其中oauth2client在源码注释中被标记为已废弃download_docs.py。认证凭据需在$HOME/.google-apis-service-account.json放置服务账号密钥并保证该账号对目标文档有访问权限。外部工具convert_docs.py依赖系统安装的pandocdownload_docs.py的-C模式同样调用pandoc生成 native 表示。网络与权限下载阶段需要访问 Google Drive/Docs API属于在线操作转换与拷贝阶段均为本地文件处理。实验性质本目录位于experiments/下README 亦将其定位为实验性自动化脚本实际发布链路中是否启用需以当前版本发布流程为准。九、小结experiments/docs_rst这套工具链完整演示了一个「云端协作编辑 → 本地自动转换 → 静态站点发布」的文档流水线设计Makefile用三个 target 把下载、转换、拷贝三个阶段串成可重复执行的命令序列download_docs.py通过 Google Drive API 多格式导出与索引页自动发现解决了「批量拉取」问题convert_docs.py以「docx 等宽字体块提取 key 匹配回填」的混合策略修复了 Pandoc 在代码块缩进上的短板copy_docs.py以手工映射表完成文档 ID 到站点路径的编排notes-about-formats.txt则为格式选型提供了数据支撑。对于任何需要以 Google Docs 为源、向静态网站发布文档的团队这套「混合转换 结构回填」的方案都值得作为参考实现。若需深入探索可继续阅读 download_docs.py、convert_docs.py、copy_docs.py 三份源码及其历史版本 experiments/docs_rst/old。【免费下载链接】beancountBeancount: Double-Entry Accounting from Text Files.项目地址: https://gitcode.com/GitHub_Trending/be/beancount创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表