下载地址:http://www.pan123.fun/share.php?id=1TqlNuVaQ3&pwd=6Y5F

一、为什么选择Python自建PDF转换工具?
在阿里云开发者社区的日常交流中,很多同学反馈以下痛点:
在线转换工具有文件大小限制,且存在数据泄露风险
商业软件授权费用高昂,个人/小团队难以承受
现有开源方案零散,缺乏工程化的批量处理和异常兜底
Python生态中的 pdf2docx 库底层基于 PyMuPDF(fitz)解析PDF结构,再通过 python-docx 重建Word文档,支持段落、表格、图片、多栏布局等元素的还原,是目前纯Python方案中效果最好的选择。
二、环境准备
bash
编辑
推荐使用阿里云镜像源加速安装
pip install pdf2docx PyMuPDF -i https://mirrors.aliyun.com/pypi/simple
验证安装
python -c "from pdf2docx import Converter; print('✅ pdf2docx ready')"
python -c "import fitz; print(f'✅ PyMuPDF version: {fitz.version}')"
⚠️ 注意:pdf2docx 适用于文字型PDF。纯扫描版/图片型PDF需结合OCR(如PaddleOCR),本文末尾会给出扩展思路。
三、核心代码实现
3.1 基础转换:3行代码搞定
python
编辑
from pdf2docx import Converter
cv = Converter("input.pdf")
cv.convert("output.docx")
cv.close()
这是最简用法,但生产环境远远不够。下面逐步增强。
3.2 工程化封装:PDFConverter类
python
编辑
"""
pdf_converter.py
PDF自动转换工具 - Python定制版
Author: 阿里云开发者社区
"""
import os
import logging
from pathlib import Path
from typing import Optional, List, Tuple
from pdf2docx import Converter
import fitz # PyMuPDF
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger(name)
class PDFConverter:
"""PDF转换工具类,支持单文件/批量/加密PDF/指定页码范围"""
def __init__(self, password: Optional[str] = None):
self.password = password
def convert_single(
self,
pdf_path: str,
docx_path: Optional[str] = None,
pages: Optional[List[int]] = None
) -> bool:
"""
转换单个PDF文件
Args:
pdf_path: PDF文件路径
docx_path: 输出docx路径,默认同名替换后缀
pages: 指定页码列表(0-indexed),None表示全部
Returns:
是否转换成功
"""
if not os.path.exists(pdf_path):
logger.error(f"文件不存在: {pdf_path}")
return False
if docx_path is None:
docx_path = str(Path(pdf_path).with_suffix(".docx"))
try:
# 处理加密PDF
if self.password:
doc = fitz.open(pdf_path)
if doc.is_encrypted:
if not doc.authenticate(self.password):
logger.error(f"密码错误,无法解密: {pdf_path}")
doc.close()
return False
logger.info(f"已解密: {pdf_path}")
doc.close()
cv = Converter(pdf_path, password=self.password)
cv.convert(docx_path, pages=pages)
cv.close()
size_kb = os.path.getsize(docx_path) / 1024
logger.info(f"✅ 转换完成: {docx_path} ({size_kb:.1f} KB)")
return True
except Exception as e:
logger.error(f"❌ 转换失败 [{pdf_path}]: {e}")
return False
def convert_batch(
self,
input_dir: str,
output_dir: Optional[str] = None,
recursive: bool = False
) -> Tuple[int, int]:
"""
批量转换目录下所有PDF
Args:
input_dir: PDF所在目录
output_dir: 输出目录,默认与输入同目录
recursive: 是否递归子目录
Returns:
(成功数, 失败数)
"""
input_path = Path(input_dir)
if not input_path.is_dir():
logger.error(f"目录不存在: {input_dir}")
return 0, 0
pattern = "**/*.pdf" if recursive else "*.pdf"
pdf_files = sorted(input_path.glob(pattern))
if not pdf_files:
logger.warning(f"未找到PDF文件: {input_dir}")
return 0, 0
logger.info(f"📂 发现 {len(pdf_files)} 个PDF文件")
success, fail = 0, 0
for pdf_file in pdf_files:
if output_dir:
out_path = Path(output_dir) / pdf_file.with_suffix(".docx").name
Path(output_dir).mkdir(parents=True, exist_ok=True)
else:
out_path = None
if self.convert_single(str(pdf_file), str(out_path) if out_path else None):
success += 1
else:
fail += 1
logger.info(f"🏁 批量转换完成: 成功={success}, 失败={fail}")
return success, fail
@staticmethod
def extract_tables(pdf_path: str, pages: Optional[List[int]] = None) -> list:
"""
提取PDF中的表格数据
Returns:
表格数据列表,每个表格为二维list
"""
cv = Converter(pdf_path)
tables = []
target_pages = pages or range(cv.num_pages)
for page_idx in target_pages:
page_tables = cv.extract_tables(page_idx)
for tbl in page_tables:
tables.append(tbl)
logger.info(f"📊 第{page_idx}页提取到表格: {len(tbl)}行 x {len(tbl[0]) if tbl else 0}列")
cv.close()
return tables
3.3 CLI命令行入口
将上面的类封装为命令行工具,方便集成到CI/CD或定时任务:
python
编辑
"""cli.py - 命令行入口"""
import argparse
from pdf_converter import PDFConverter
def main():
parser = argparse.ArgumentParser(description="PDF自动转换工具 v1.0")
subparsers = parser.add_subparsers(dest="command", help="子命令")
# 单文件转换
p_single = subparsers.add_parser("convert", help="转换单个PDF")
p_single.add_argument("pdf", help="PDF文件路径")
p_single.add_argument("-o", "--output", help="输出docx路径")
p_single.add_argument("-p", "--pages", nargs="+", type=int, help="指定页码(0-indexed)")
p_single.add_argument("--password", help="PDF密码")
# 批量转换
p_batch = subparsers.add_parser("batch", help="批量转换目录下的PDF")
p_batch.add_argument("input_dir", help="PDF目录")
p_batch.add_argument("-o", "--output-dir", help="输出目录")
p_batch.add_argument("-r", "--recursive", action="store_true", help="递归子目录")
p_batch.add_argument("--password", help="PDF密码")
# 表格提取
p_table = subparsers.add_parser("tables", help="提取PDF表格")
p_table.add_argument("pdf", help="PDF文件路径")
p_table.add_argument("-p", "--pages", nargs="+", type=int, help="指定页码")
args = parser.parse_args()
converter = PDFConverter(password=getattr(args, "password", None))
if args.command == "convert":
converter.convert_single(args.pdf, args.output, args.pages)
elif args.command == "batch":
converter.convert_batch(args.input_dir, args.output_dir, args.recursive)
elif args.command == "tables":
tables = PDFConverter.extract_tables(args.pdf, args.pages)
for i, tbl in enumerate(tables):
print(f"\n=== 表格 {i+1} ===")
for row in tbl:
print(row)
else:
parser.print_help()
if name == "main":
main()
使用示例:
bash
编辑
单文件转换
python cli.py convert report.pdf -o result.docx
只转换第1、3、5页
python cli.py convert report.pdf --pages 0 2 4
批量转换(含子目录)
python cli.py batch ./pdfs -o ./docs -r
加密PDF
python cli.py convert secret.pdf --password mypass123
提取表格
python cli.py tables invoice.pdf -p 0 1
四、进阶:扫描件PDF的OCR扩展思路
对于纯图片型PDF,pdf2docx 无法识别文字。推荐组合方案:
python
编辑
pip install paddlepaddle paddleocr PyMuPDF
import fitz
from paddleocr import PaddleOCR
def ocr_pdf_to_text(pdf_path: str) -> str:
"""扫描件PDF → 文本(OCR)"""
ocr = PaddleOCR(use_angle_cls=True, lang="ch")
doc = fitz.open(pdf_path)
full_text = []
for page_num in range(len(doc)):
pix = doc[page_num].get_pixmap(dpi=300)
img_bytes = pix.tobytes("png")
result = ocr.ocr(img_bytes, cls=True)
page_lines = [line[1][0] for line in result[0] if line[1]]
full_text.append("\n".join(page_lines))
doc.close()
return "\n\n".join(full_text)
💡 阿里云用户提示:如果OCR量大,建议使用阿里云OCR API替代本地PaddleOCR,避免GPU资源开销,按量付费更灵活。
五、常见问题与避坑指南
表格
问题 原因 解决方案
转换后格式错乱 PDF使用了特殊编码/字体 尝试用PyMuPDF先转为标准PDF再转换
内存溢出 大文件(>200MB)一次性加载 分页转换:cv.convert(out, pages=[i])
表格识别不全 无边线表格 改用camelot或tabula-py专门提取表格
中文乱码 缺少中文字体嵌入 确保源PDF嵌入了字体,或用OCR兜底
多线程报错 pdf2docx非线程安全 使用多进程multiprocessing.Pool替代
六、总结
本文提供了一套完整的Python PDF转换工具代码,核心要点回顾:
pdf2docx 负责文字型PDF→Word的高保真转换
PDFConverter类 封装了单文件、批量、加密、表格提取四大场景
CLI入口 让工具可直接集成到自动化流水线
OCR扩展 覆盖扫描件场景,形成完整闭环
整套代码约200行,无外部服务依赖,适合部署在阿里云ECS、函数计算FC或本地服务器。如果你有特殊的PDF转换需求(如保留批注、合并拆分、水印添加),欢迎在评论区交流,后续可以出专题文章。