Daily Build

我是怎么从国标电子书库抓取 PDF 并整理书签的

工作中经常需要查阅各种标准和图集。国标电子书库(ebook.chinabuilding.com.cn)是常用的来源之一,但它仅提供在线阅读 PDF 文档少量页面,未开放全文下载。此外,某些 PDF,尤其是从其他渠道获取的扫描版 PDF 从零开始手动创建书签非常消磨个人的时间和精力。

工作一段时间后,我编写了 3 个脚本帮助抓取和处理 PDF 文档。

std.py 通过爬取网站获取 PDF 文件,本质上是一个 WebCrawler。std.py bookmark 抓取国标电子书库网站对应书籍的目录、经格式化规则处理后写入 PDF——产出是书签的初版,随后在 Acrobat 中人工微调(章节分组、文本校对、个别条目修改)即可,省去了从零开始建立书签的大量时间。std-ocr.py 针对各种来源的扫描版 PDF,提供半自动化的逐条建立书签方案。std.py copy-bookmarkstd-copy-bookmark.ps1 针对另一类场景:当用户已完成书签整理,但需通过 Acrobat 等 PDF 编辑软件进行移除隐藏信息、统一图片分辨率等预处理操作时,原有书签可能会一并丢失,这两个工具用于将书签从备份恢复至处理后的文件——后者是为了批量补做历史 PDF 的预处理工作而编写的。

三个脚本相互独立,按场景选用。

准备

需要 Python ≥ 3.13,建议使用 uv。两个 Python 脚本均通过文件头部的 PEP 723 元信息声明依赖,uv run 会自动建立临时环境完成安装,无需配置 pip 或 venv。

uv run std.py <command> [args]
uv run std-ocr.py

如不使用 uv,亦可手动执行 pip install httpx parsel pymupdf keyboard pyperclip 安装相应依赖。

ps1 脚本需要 PowerShell 7+。

选择指引

工作流 选用脚本
抓取国标 PDF / 生成书签初版 std.py download/std.py bookmark
为扫描版 PDF 半自动创建书签 std-ocr.py
Acrobat 清理操作后恢复已整理的书签(单文件) std.py copy-bookmark
Acrobat 清理操作后恢复已整理的书签(批量) std-copy-bookmark.ps1("发送到"菜单)

下文按脚本逐一展开。

std.py

提供三个子命令:downloadbookmarkcopy-bookmark

download 抓取 PDF 文件;bookmark 从国标电子书库网站抓取对应目录、经格式化处理后覆盖 PDF 自带书签,产出是书签的初版,便于后续在 Acrobat 中手动微调;copy-bookmark 在分页一致的两个 PDF 之间迁移书签。

Book ID 的获取

打开任意书籍详情页,URL 中 bookID= 参数对应的数字即为 Book ID:

https://ebook.chinabuilding.com.cn/zbooklib/book/detail/show?SiteID=1&bookID=12345

下文命令中的 --book-id 均指该值。

用法

# 抓取 PDF(保存为 12345.pdf)
uv run std.py download --book-id 12345

# 将当前目录下 12345.pdf 自带的书签覆盖为国标电子书库网站对应目录经格式化处理后的版本
uv run std.py bookmark --book-id 12345

# 在两个 PDF 之间迁移书签
uv run std.py copy-bookmark --source 12345.pdf --target 12345-cleaned.pdf

bookmark 子命令按约定处理当前目录下的 {book_id}.pdf,因此无需 --input 参数。

实现要点

std-ocr.py

为其他来源的 PDF 文件(典型场景是扫描版)半自动建立书签:用户对照 PDF 目录页逐条操作,按 F11 触发截屏 OCR 并自动建立书签。

国标电子书库本身的扫描版书籍因详情页缺少结构化目录、std.py bookmark 对其无效,也属于本脚本的适用范围。

前置条件

用法

uv run std-ocr.py

启动后常驻后台监听以下热键:

热键 作用
F11 截屏 OCR → 自动新建书签
F12 切换文本格式化开关
CTRL C 退出

工作流程:

  1. 在 Acrobat 中跳转至目录条目对应的实际页面(非目录页本身——书签的目标位置即为按下 F11 时当前显示的页)。
  2. F11 进入 Umi-OCR 截图模式。
  3. 框选目录页中对应条目的文字,松开鼠标。
  4. 脚本依次发送 CTRL B 新建书签 → 将 OCR 结果粘贴至标题栏 → 回车确认。
  5. 翻至下一条对应页面,重复上述步骤。

每次建立书签均需同时满足两个条件:Acrobat 当前页停留于书签目标位置屏幕上可见对应的目录条目文字。常用的操作姿势是 Acrobat 双窗口或分屏,一侧显示目录页,另一侧翻至目标页。

关于格式化

OCR 结果常包含多余空格与换行(同一行文字识别后字与字之间常被插入空格)。默认开启的格式化逻辑包括:

  1. 删除全部空白字符;
  2. 在 "第 X 章/节/条/款/项"、"附录/附件/附表/附图/表/图 + 编号"、1.2.3 形式的数字编号、A.1.2 形式的字母编号后补一个空格;
  3. 当识别结果为单独的"条文说明"或"起草说明"时,替换为"附:条文说明""附:起草说明"——此为国标中作为附件标题的常见写法。

如需对某条目录跳过格式化(例如较长的说明性文字),可按 F12 临时关闭,处理完毕后再次按 F12 恢复。

实现要点

std-copy-bookmark.ps1

std.py copy-bookmark 单次仅处理一对文件。本 ps1 脚本配合 Windows "发送到"菜单,可在一次操作中处理多个文件,主要用于批量补做此前未经过 Acrobat 预处理的历史 PDF——为这类文件统一执行清理操作时,已整理的书签会丢失,本脚本将其从副本恢复回处理后的文件。

用法

将 ps1 置于固定位置(例如 D:\tools\std\std-copy-bookmark.ps1),修改文件顶部的 $StdScript 常量使其指向 std.py 的绝对路径,然后注册至"发送到"菜单(步骤见下节)。

注册完成后的日常工作流:

  1. 复制原 PDF:在资源管理器中执行 CTRL C / CTRL V,Windows 默认将副本命名为 xxx - 副本.pdf。该副本作为书签源文件保留备用,不应做任何修改。
  2. 在 Acrobat 中清理原文件(不带"- 副本"后缀的文件),执行移除隐藏信息、统一图片分辨率等预处理操作并保存。此步骤将导致原文件已整理的书签丢失。
  3. 选中一个或多个清理过的 PDF(不带"- 副本"后缀的),右键 → 发送到 → ps1 快捷方式。
  4. 脚本对每个目标文件自动定位对应的 xxx - 副本.pdf 作为书签源,调用 std.py copy-bookmark,成功后删除副本

最终结果:原文件名保持不变,书签从副本迁移过来,副本被清理。

注册至"发送到"菜单

  1. std-copy-bookmark.ps1 置于固定位置,例如 D:\tools\std\std-copy-bookmark.ps1
  2. 编辑 ps1 顶部的 $StdScript 常量,使其指向实际的 std.py 绝对路径。
  3. 在 ps1 文件上右键 → 发送到 → 桌面快捷方式。
  4. 右键新建的快捷方式 → 属性 → "目标"栏改为:
pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "D:\tools\std\std-copy-bookmark.ps1"
  1. 重命名为合适的名称,例如"复制 PDF 书签"。
  2. Win+R 打开运行对话框,输入 shell:sendto 回车,将该快捷方式剪切至打开的文件夹中。

完成后右键任意 PDF 文件即可在"发送到"菜单中看到该项。

实现要点

配合使用

下文列出几种典型工作流的具体步骤。

工作流一:抓取国标 PDF 并整理书签

完整流程为四步:

  1. 抓取 PDF
uv run std.py download --book-id 12345
  1. 在 Acrobat 中预处理:打开 12345.pdf,执行移除隐藏信息、统一图片分辨率等预处理操作并保存。
  2. 生成书签初版
uv run std.py bookmark --book-id 12345
  1. 在 Acrobat 中微调书签:根据需要进行章节分组、文本校对、个别条目修改等。

第 3 步生成的是书签的初版(覆盖 PDF 自带的网站书签),格式经过规则处理但不分层级。第 4 步的人工调整是最终成型的关键,但相比从零开始建立书签,节省的时间相当可观。

工作流二:为扫描版 PDF 添加书签

来自其他渠道的扫描版 PDF,或国标电子书库中的扫描版 PDF——std.py bookmark 对此类文件无效,改用 OCR 流程:

uv run std-ocr.py

随后在 Acrobat 中打开 PDF,按 std-ocr.py 章节描述的流程逐条建立书签。完成后在 Acrobat 中按 CTRL S 保存。

工作流三:批量补做历史 PDF 的清理

针对此前未经 Acrobat 预处理的一批已整理书签的 PDF,统一补做清理:

  1. 在资源管理器中全选这批 PDF,CTRL C / CTRL V,得到一组 xxx - 副本.pdf
  2. 在 Acrobat 中依次打开原文件(非副本),执行清理操作并保存。此步骤将导致原文件的书签丢失。
  3. 全部清理完成后,全选所有原文件(不要选中副本),右键 → 发送到 → 复制 PDF 书签。
  4. 等待脚本执行完毕,副本自动删除,原文件书签恢复。

期间任何文件清理失败或书签复制失败,对应的副本将被保留,脚本结束时会列出,可手动处理。

故障排查

std.py download 失败或抓不到 PDF 链接。 通常是网站改版所致。检查 get_pdf_url 中的 CSS 选择器是否仍能匹配——打开试读页源码,搜索 link rel="stylesheet",对比路径结构是否变化。

std.py bookmark 抓取的目录为空或错乱。 同样可能是网站改版,应检查 get_tocdiv.m-t-md.font14 div.clearfix div::text 选择器。如仅为部分条目错位或编号识别异常,则可能是 TOC_FORMAT_RULES 覆盖不全,应补充相应正则。

std-ocr.pyF11无反应。 三种可能原因:(1) Umi-OCR 未启动;(2) HTTP 服务未启用;(3) 端口非默认 1224——应修改UMI_OCR_SERVICE 常量。控制台日志中出现 "OCR request failed" 对应 (2),出现 httpx 连接错误对应 (1) 或 (3)。

std-ocr.py 建立的书签标题正确,但目标页错误。 按下 F11时 Acrobat 当前显示的页面即为书签指向的页面——此为 AcrobatCTRL B的行为,非脚本可控。每次按F11 前应先翻至目标页。

std-copy-bookmark.ps1 报 "std.py not found"。 修改 ps1 顶部的 $StdScript 常量为正确的路径。

std-copy-bookmark.ps1 报 "Source not found"。 检查目标文件所在目录是否存在对应的 xxx - 副本.pdf。如系统语言非简体中文,副本可能采用其他命名(例如 xxx - Copy.pdf),应修改 ps1 中构造 $sourceFile 的字符串。

下载

本文介绍的三个脚本可通过百度网盘分享链接获取:https://pan.baidu.com/s/13nvO_EvPq83FUauo96fINg?pwd=uy8v

使用前请参照"准备"一节安装 uv,并按各脚本"前置条件"小节确认 Umi-OCR、Adobe Acrobat 等依赖项已就位。


本文采用 CC BY-NC-SA 4.0 协议发布,可自由转载、修改,但需保留作者署名、不可用于商业用途、衍生作品需以相同协议发布。