Python 自动化之 PDF 合并拆分与格式转换——进阶实战

上一篇讲了 PDF 文字提取和表格处理,这篇讲 PDF 的进阶操作------合并、拆分、格式互转(PDF→Word、PDF→Excel)、批量压缩。

一、PDF 合并

把多个 PDF 合并成一个,适合用来汇总报告、合并扫描件。

批量合并整个文件夹

python 复制代码
from PyPDF2 import PdfMerger
import os

def merge_all_pdfs(input_dir, output_file):
    """合并整个文件夹下的所有 PDF"""
    merger = PdfMerger()

    # 按文件名排序(保证合并顺序)
    files = sorted([
        f for f in os.listdir(input_dir)
        if f.lower().endswith(".pdf")
    ])

    if not files:
        print("没有找到 PDF 文件")
        return

    for f in files:
        filepath = os.path.join(input_dir, f)
        merger.append(filepath)
        print(f"已添加: {f}")

    merger.write(output_file)
    merger.close()
    print(f"合并完成!共 {len(files)} 个文件 → {output_file}")

# 使用
merge_all_pdfs("待合并的PDF", "汇总结果.pdf")

按指定顺序合并

python 复制代码
def merge_selected_pdfs(file_list, output_file):
    """按指定顺序合并 PDF"""
    merger = PdfMerger()

    for f in file_list:
        if os.path.exists(f):
            merger.append(f)
            print(f"已添加: {os.path.basename(f)}")

    merger.write(output_file)
    merger.close()
    print(f"合并完成 → {output_file}")

# 使用:顺序自定义
merge_selected_pdfs([
    "封面.pdf",
    "目录.pdf",
    "正文.pdf",
    "附录.pdf",
], "完整报告.pdf")

二、PDF 拆分

1. 按页数拆分(每 N 页一份)

python 复制代码
from PyPDF2 import PdfReader, PdfWriter
import os

def split_by_pages(input_file, output_dir, pages_per_file=10):
    """
    按每 N 页一组拆分 PDF
    适合:把几百页的 PDF 拆成多份小文件
    """
    os.makedirs(output_dir, exist_ok=True)
    reader = PdfReader(input_file)
    total = len(reader.pages)
    file_index = 1

    for start in range(0, total, pages_per_file):
        writer = PdfWriter()
        end = min(start + pages_per_file, total)

        for i in range(start, end):
            writer.add_page(reader.pages[i])

        output = os.path.join(
            output_dir,
            f"第{file_index}部分_第{start+1}-{end}页.pdf"
        )

        with open(output, "wb") as f:
            writer.write(f)

        print(f"已生成: {os.path.basename(output)} ({end-start}页)")
        file_index += 1

    print(f"拆分完成,共 {file_index - 1} 个文件")

# 使用:每 20 页拆一份
split_by_pages("大文件.pdf", "拆分输出", pages_per_file=20)

2. 提取指定页面

python 复制代码
def extract_pages(input_file, output_file, page_numbers):
    """
    提取 PDF 中的指定页码范围
    page_numbers: 从 1 开始的页号列表,如 [1, 3, 5] 或 range(1, 6)
    """
    reader = PdfReader(input_file)
    writer = PdfWriter()

    for i in page_numbers:
        # page_numbers 从 1 开始,reader 从 0 开始
        writer.add_page(reader.pages[i - 1])

    with open(output_file, "wb") as f:
        writer.write(f)

    print(f"已提取 {len(page_numbers)} 页 → {output_file}")

# 使用
extract_pages("报告.pdf", "摘要.pdf", [1, 2, 3])     # 提取前 3 页
extract_pages("报告.pdf", "附录.pdf", range(48, 55))  # 提取第 48-54 页

三、PDF 转 Word

方法一:pdfplumber + python-docx(文字型 PDF)

python 复制代码
import pdfplumber
from docx import Document

def pdf_to_word(pdf_path, word_path):
    """文字型 PDF 转 Word(保留文本)"""
    doc = Document()

    with pdfplumber.open(pdf_path) as pdf:
        for page in pdf.pages:
            text = page.extract_text()
            if text:
                doc.add_paragraph(text.strip())

    doc.save(word_path)
    print(f"转换完成: {word_path}")

# 使用
pdf_to_word("报告.pdf", "报告.docx")

方法二:pdf2docx(保留格式,推荐)

bash 复制代码
pip install pdf2docx
python 复制代码
from pdf2docx import Converter

def pdf_to_word_with_format(pdf_path, word_path):
    """PDF 转 Word(保留表格、排版)"""
    cv = Converter(pdf_path)
    cv.convert(word_path, start=0, end=None)
    cv.close()
    print(f"转换完成: {word_path}")

# 使用
pdf_to_word_with_format("报告.pdf", "报告.docx")

pdf2docx 的转换质量更高,能保留表格和基本排版。如果遇到表格多的 PDF,优先用这个。

四、PDF 转 Excel(提取表格)

python 复制代码
import pdfplumber
import pandas as pd

def pdf_tables_to_excel(pdf_path, excel_path):
    """从 PDF 中提取所有表格并导出到 Excel"""
    with pdfplumber.open(pdf_path) as pdf:
        all_tables = []

        for page_num, page in enumerate(pdf.pages, 1):
            tables = page.extract_tables()
            for table_idx, table in enumerate(tables, 1):
                # 第一行为表头
                df = pd.DataFrame(
                    table[1:],
                    columns=table[0] if table else None
                )
                all_tables.append({
                    "sheet_name": f"第{page_num}页_表格{table_idx}",
                    "data": df
                })
                print(f"第 {page_num} 页找到表格{table_idx},{len(table)}行×{len(table[0])}列")

    # 写入 Excel(每个表格一个 sheet)
    with pd.ExcelWriter(excel_path) as writer:
        for t in all_tables:
            sheet = t["sheet_name"][:31]  # Excel sheet 名最长 31 字符
            t["data"].to_excel(writer, sheet_name=sheet, index=False)

    print(f"导出完成,共 {len(all_tables)} 个表格 → {excel_path}")

# 使用
pdf_tables_to_excel("财务报表.pdf", "财务报表数据.xlsx")

五、PDF 压缩

bash 复制代码
pip install pypdf
python 复制代码
from pypdf import PdfReader, PdfWriter

def compress_pdf(input_path, output_path):
    """压缩 PDF 文件大小"""
    reader = PdfReader(input_path)
    writer = PdfWriter()

    for page in reader.pages:
        writer.add_page(page)

    # 压缩内容流
    for page in writer.pages:
        page.compress_content_streams()

    with open(output_path, "wb") as f:
        writer.write(f)

    original_size = os.path.getsize(input_path)
    compressed_size = os.path.getsize(output_path)
    ratio = (1 - compressed_size / original_size) * 100

    print(f"压缩完成: {original_size//1024}KB → {compressed_size//1024}KB (压缩 {ratio:.0f}%)")

def batch_compress(input_dir, output_dir):
    """批量压缩 PDF"""
    os.makedirs(output_dir, exist_ok=True)

    for f in os.listdir(input_dir):
        if not f.lower().endswith(".pdf"):
            continue

        input_path = os.path.join(input_dir, f)
        output_path = os.path.join(output_dir, f)
        compress_pdf(input_path, output_path)

# 使用
batch_compress("原始PDF", "压缩后PDF")

六、PDF 提取图片

python 复制代码
import zipfile
import os
from PIL import Image

def extract_images_from_pdf(pdf_path, output_dir):
    """从 PDF 中提取所有图片"""
    os.makedirs(output_dir, exist_ok=True)

    # PDF 本质上是 ZIP 文件,图片在 word/media 目录下
    with zipfile.ZipFile(pdf_path, "r") as z:
        image_count = 0
        for name in z.namelist():
            if name.startswith("word/media/") or name.startswith("media/"):
                ext = os.path.splitext(name)[1].lower()
                if ext in (".png", ".jpg", ".jpeg", ".gif", ".tiff"):
                    # 提取图片
                    z.extract(name, output_dir)
                    # 重命名到根目录
                    src = os.path.join(output_dir, name)
                    dst = os.path.join(output_dir, f"image_{image_count}{ext}")
                    os.rename(src, dst)
                    image_count += 1

    # 清理空目录
    for root, dirs, files in os.walk(output_dir, topdown=False):
        if not files and not dirs:
            os.rmdir(root)

    print(f"提取完成,共 {image_count} 张图片 → {output_dir}")

# 使用
extract_images_from_pdf("带图片的文档.pdf", "提取的图片")

七、PDF 添加水印

python 复制代码
from PyPDF2 import PdfReader, PdfWriter

def add_watermark(input_pdf, watermark_pdf, output_pdf):
    """给 PDF 每一页添加水印"""
    reader = PdfReader(input_pdf)
    watermark = PdfReader(watermark_pdf)
    watermark_page = watermark.pages[0]

    writer = PdfWriter()

    for page in reader.pages:
        page.merge_page(watermark_page)
        writer.add_page(page)

    with open(output_pdf, "wb") as f:
        writer.write(f)

    print(f"水印添加完成 → {output_pdf}")

# 使用
add_watermark("合同.pdf", "水印.pdf", "合同_带水印.pdf")

八、批处理完整模板

python 复制代码
import os
from PyPDF2 import PdfReader, PdfWriter, PdfMerger

class PDFBatchProcessor:
    """PDF 批量处理器"""

    def __init__(self, input_dir, output_dir):
        self.input_dir = input_dir
        self.output_dir = output_dir
        os.makedirs(output_dir, exist_ok=True)

    def get_pdf_files(self):
        """获取所有 PDF 文件"""
        return [
            f for f in os.listdir(self.input_dir)
            if f.lower().endswith(".pdf")
        ]

    def merge_all(self, output="合并结果.pdf"):
        """合并所有 PDF"""
        files = self.get_pdf_files()
        merger = PdfMerger()
        for f in sorted(files):
            merger.append(os.path.join(self.input_dir, f))
        merger.write(os.path.join(self.output_dir, output))
        merger.close()
        print(f"合并 {len(files)} 个文件 → {output}")

    def split_all(self, pages_per_file=10):
        """批量拆分每个 PDF"""
        for f in self.get_pdf_files():
            input_path = os.path.join(self.input_dir, f)
            name = os.path.splitext(f)[0]
            sub_dir = os.path.join(self.output_dir, name)
            os.makedirs(sub_dir, exist_ok=True)

            reader = PdfReader(input_path)
            total = len(reader.pages)

            for start in range(0, total, pages_per_file):
                writer = PdfWriter()
                end = min(start + pages_per_file, total)
                for i in range(start, end):
                    writer.add_page(reader.pages[i])

                output = os.path.join(
                    sub_dir,
                    f"{name}_p{start+1}-{end}.pdf"
                )
                with open(output, "wb") as f_out:
                    writer.write(f_out)

            print(f"{f}: {total}页 → {sub_dir}/")

    def rotate_all(self, rotation=90):
        """批量旋转所有 PDF 页面"""
        for f in self.get_pdf_files():
            input_path = os.path.join(self.input_dir, f)
            output_path = os.path.join(self.output_dir, f"rotated_{f}")

            reader = PdfReader(input_path)
            writer = PdfWriter()

            for page in reader.pages:
                page.rotate(rotation)
                writer.add_page(page)

            with open(output_path, "wb") as f_out:
                writer.write(f_out)

            print(f"旋转完成: {f}")

# 使用
processor = PDFBatchProcessor("输入PDF", "输出PDF")
processor.merge_all()        # 合并
# processor.split_all(5)     # 拆分(每5页一份)
# processor.rotate_all(90)   # 旋转(顺时针90度)

总结

PDF 处理的常见需求基本都覆盖了:

复制代码
合并 → PdfMerger.append()
拆分 → PdfWriter 按页提取
转Word → pdf2docx
转Excel → pdfplumber 提取表格
压缩 → pypdf compress_content_streams
水印 → merge_page()
提取图片 → zipfile 解压

遇到批量处理时,把需要的功能组合成一个函数,一条命令搞定几百个 PDF。


💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。