竖排繁体 PDF 的阅读门槛很高。古籍、民国文献、港台早期学术期刊中,文字从上到下排列,列与列之间从右向左推进。普通 PDF 阅读器只能原样显示,对习惯横排简体的读者来说,视线需要不断逆时针旋转,阅读效率极低。更麻烦的是,当你想把这类文档导入笔记软件或知识库时,绝大多数工具会直接拒绝:它们没有竖排渲染能力,甚至连基本的文本复制都无能为力。
传统 OCR 工具在这个场景下的表现更差。它们的默认假设是文本从左到右、从上到下排列。面对竖排页面,这些工具通常按坐标排序——先扫最上面一行,再扫下一行——结果把原本属于右列的文字和左列的文字混成一段,逻辑完全打乱。你得到的是一堆”认得出字、读不懂句”的碎片化文本,人工整理的时间往往比重新打字还长。
MinerU 的处理逻辑不同。它把文档解析当成视觉理解任务,而不是单纯的字符识别。MinerU2.5 的 VLM 后端在布局检测阶段就同步预测每个文本块的旋转角度和阅读顺序,这意味着竖排文本列会被识别为旋转元素,并按从右到左的列优先顺序输出。配合 mineru-open-sdk,你只需要几行 Python 代码就能提取出结构化的 Markdown,再交给大模型做一次繁简转换和横排重排,整个流程在本地即可完成。
为什么选 MinerU
MinerU2.5 的架构设计把布局分析重新定义为一个多任务预测问题。在第一阶段,模型对下采样的页面图像进行全局布局分析,同时输出四个维度:边界框坐标、元素类别、旋转角度、阅读顺序。这四个预测在单次前向传播中完成,而不是像传统流水线那样先检测、再识别、最后才用规则排序。
旋转角度的预测是处理竖排的核心。MinerU2.5 的技术报告 Appendix A.1 中,布局检测的输出格式包含一个 <|rotate_dir|> token,用来标记文本方向。标准直立文本标记为 <|rotate up|>,其他方向则对应旋转内容。在实际的模型输出层,每个内容块还附带一个 angle 字段,取值被离散化为四个角度:0(向上)、90(向右)、180(颠倒)、270(向左)。竖排文本列通常对应 90° 或 270° 的旋转标记。
这种设计的意义在于:竖排文本不会在 OCR 阶段被当成横排误读。模型在识别字符之前,已经知道这块文字是竖着的,阅读顺序应该从该列的顶部向下、再从右列向左列推进。这与传统 OCR 先识别再事后调整方向的策略有本质区别。
你不需要在本地部署复杂的模型栈。mineru-open-sdk 默认调用云端的 Precision Extract 接口,该接口后端就是 MinerU2.5 的 VLM 模型。这意味着你只需一个 API Token,就能获得上述旋转识别和阅读顺序恢复的能力,无需关心 GPU 显存、模型版本或 backend 差异。
在主流开源文档解析工具中,MinerU2.5 的 VLM 后端明确把旋转角度和阅读顺序作为布局检测的原生输出维度。对于竖排繁体 PDF,它提供的不是事后补丁,而是架构层面的识别能力。
方案架构:MinerU + LLM 两步走
整个方案可以拆解为两个独立阶段:
竖排繁体 PDF
│
▼
[MinerU 提取层] ──► 结构化 Markdown(保留段落边界与阅读顺序)
│
▼
[DeepSeek V4 Flash 转换层] ──► 横排简体 Markdown第一阶段交给 MinerU,是因为它在文档解析上有明确的能力边界:它擅长看懂版面结构、恢复阅读顺序、提取文字和表格,但不负责语言层面的改写。繁体转简体涉及字符映射、词汇替换(如”軟體”→”软件”)和语序微调,这些属于语言模型的强项,不是文档解析模型的职责范围。
第二阶段交给 DeepSeek V4 Flash,是因为它成本低、响应快,且对中文的理解能力足够处理繁简转换和竖排重排这类结构化改写任务。
两阶段分离还有一个好处:如果 MinerU 的提取结果中某段竖排文本的识别有瑕疵,你可以在 DeepSeek 的 Prompt 中明确要求”只转换确认的文本,保留原文中的生僻字或不确定字符”,避免大模型为了通顺而过度发挥。
实战:用 MinerU Python SDK 提取内容
安装只需要一条命令:
pip install mineru-open-sdkmineru-open-sdk 是 MinerU 官方提供的 Python 客户端,完全免费。它封装了云 API 的阻塞式调用和异步轮询,开箱即用。
以下是一段完整的提取代码:
from mineru import MinerU
# 从 https://mineru.net/apiManage/token 获取免费 Token
client = MinerU("your-api-token")
# 对竖排繁体扫描件,建议显式开启 OCR 并指定中文
result = client.extract(
"./古籍扫描件.pdf",
model="vlm", # VLM 后端,支持旋转角度预测
ocr=True, # 必须开启,扫描件没有文本层
language="ch", # 中文(含繁体)识别
pages="1-20", # 可按页码范围处理大文档
)
# 提取结果
print(result.markdown)
# 保存完整结果包(含图片、表格、JSON 元数据)
result.save_all("./output/")代码中有三个参数对竖排场景尤为重要:
-
ocr=True:竖排繁体 PDF 多为扫描件,页面本质是图片而非文本层。不开启 OCR,你只会得到空内容或乱码。 -
language="ch":云端默认就是中文,但显式声明可以确保模型加载针对中文优化的识别权重,覆盖简体、繁体和竖排变体。 -
model="vlm":指定 VLM 后端。这是获取旋转角度和阅读顺序恢复能力的前提。虽然云端的extract()方法在省略model时会自动推断,但显式写出vlm可以消除歧义。
如果你只是快速预览效果,也可以先用 flash_extract(),它不需要 Token:
client = MinerU() # 免登录
result = client.flash_extract("./古籍扫描件.pdf")
print(result.markdown)flash_extract 的优势是零配置、速度快,但文件大小限制在 10 MB、页数限制在 20 页,且 OCR 默认关闭。对于高清扫描的古籍,建议还是用 extract() 并传入 Token。
提取完成后,result.markdown 中的文本已经按阅读顺序排列。对于竖排页面,MinerU 会把右列内容放在前面、左列内容放在后面,列内保持从上到下。这个顺序是后续横排转换的基础。
实战:DeepSeek V4 Flash 做繁简转换与重排
DeepSeek V4 Flash 的调用采用标准的 OpenAI 兼容接口。以下是一个完整的转换脚本:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
)
def convert_vertical_to_horizontal(markdown_text: str) -> str:
prompt = f"""你是一名古籍数字化专家。请将以下从竖排繁体 PDF 中提取的 Markdown 文本,转换为横排简体中文。
要求:
1. 繁体字转为简体字(保留必要的原文异体字或生僻字标注)。
2. 竖排的阅读顺序已经由上游工具恢复为"从上到下、从右到左"的列优先顺序。请将其重排为标准的横排阅读顺序:"从左到右、从上到下"的段落流。
3. 保持原有的段落边界和标题层级,不要合并段落。
4. 如果原文中有表格,保留 Markdown 表格格式。
5. 不要添加任何解释、总结或元评论。
待转换文本:
{markdown_text}
"""
response = client.chat.completions.create(
model="deepseek-chat", # 或 deepseek-v4-flash,视账号可用模型而定
messages=[{"role": "user", "content": prompt}],
temperature=0.1, # 低温度,减少模型发挥
)
return response.choices[0].message.content
# 使用
converted = convert_vertical_to_horizontal(result.markdown)
with open("./output/横排简体.md", "w", encoding="utf-8") as f:
f.write(converted)Prompt 设计的核心在于把重排规则说清楚。竖排到横排不是简单的字符替换,阅读顺序的重建才是关键。你必须明确告诉模型:”上游已经按列优先顺序排好了,你只需要按段落流展开。”否则模型可能会自作聪明地重新排序,导致逻辑混乱。
temperature=0.1 的设置是为了抑制大模型的创造性。繁简转换和格式重排是一个确定性任务,不需要发散。如果输出中出现模型自己补充的过渡句或解释段落,说明温度还是太高,可以降到 0。
成本方面,DeepSeek V4 Flash 的定价极低,百万 token 输入通常在几元人民币级别。一本 300 页的古籍扫描件,提取后的 Markdown 文本量大约在 10-20 万 token,单次转换成本可以控制在 1 元以内。
完整脚本与进阶技巧
把前两步合并,得到一个可直接运行的端到端脚本:
import os
from mineru import MinerU
from openai import OpenAI
MINERU_TOKEN = os.getenv("MINERU_TOKEN")
DEEPSEEK_KEY = os.getenv("DEEPSEEK_API_KEY")
mineru_client = MinerU(MINERU_TOKEN)
llm_client = OpenAI(api_key=DEEPSEEK_KEY, base_url="https://api.deepseek.com/v1")
def process_pdf(pdf_path: str, output_dir: str = "./output"):
# 1. MinerU 提取
result = mineru_client.extract(
pdf_path,
model="vlm",
ocr=True,
language="ch",
)
# 2. DeepSeek 繁简转换与重排
prompt = f"""将以下竖排繁体 Markdown 转为横排简体。保持段落边界,不要添加元评论。
{result.markdown}
"""
converted = llm_client.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": prompt}],
temperature=0.1,
).choices[0].message.content
# 3. 保存
os.makedirs(output_dir, exist_ok=True)
basename = os.path.splitext(os.path.basename(pdf_path))[0]
with open(f"{output_dir}/{basename}_横排简体.md", "w", encoding="utf-8") as f:
f.write(converted)
# 可选:同时保存原始提取结果,供人工校对
result.save_markdown(f"{output_dir}/{basename}_原始提取.md")
# 批量处理
for pdf in ["古籍1.pdf", "古籍2.pdf"]:
process_pdf(pdf)对于超过 200 页的大部头文献,可以在 extract() 中分块处理:
# 先处理前 50 页
result = mineru_client.extract(pdf_path, model="vlm", ocr=True, language="ch", pages="1-50")
# 再处理后 50 页
result = mineru_client.extract(pdf_path, model="vlm", ocr=True, language="ch", pages="51-100")分段处理有两个好处:一是避免单任务超时(默认 300 秒),二是如果某页识别异常,不会导致整本书的失败。
mineru-open-sdk 还支持上下文管理器,确保连接正确关闭:
with MinerU(MINERU_TOKEN) as client:
result = client.extract("./古籍.pdf", ocr=True, language="ch")效果评估与局限
这个方案最适合的场景有三类:一是民国期刊和古籍的数字化阅读,二是港台早期学术论文的繁简转换,三是需要把竖排文献导入现代知识库(如 Obsidian、Notion、Dify)的批量处理。
它的局限同样明显。首先是输入质量:MinerU 的 OCR 对清晰印刷体效果最好,对手写批注、严重模糊的扫描件或艺术字体的识别率会显著下降。其次是版式复杂度:如果一页中同时存在竖排正文、横排注释、嵌入式表格和跨页插图,阅读顺序的恢复可能出现局部错乱。最后是繁简转换的边界:DeepSeek 在处理古汉语词汇(如”於”vs”于”、”後”vs”后”)时,可能会按现代汉语习惯过度简化,改变原意。
如果你处理的文档以手写体为主,或者对竖排识别准确率要求极高,可以考虑替代方案:先用 PaddleOCR 的竖排模型做字符级识别,再自己写规则重建阅读顺序。这个路径更灵活,但需要更多的工程投入。
对于绝大多数开发者来说,MinerU SDK + DeepSeek 的两步走方案已经足够。它的核心价值不是追求 100% 的识别准确率,而是把”从竖排繁体 PDF 到横排简体文本”这个原本需要多工具拼接的复杂流程,压缩成了两段 Python 调用。