乐于分享
好东西不私藏

一批 PDF 年报怎么快速拆?Python 自动提取表格字段

一批 PDF 年报怎么快速拆?Python 自动提取表格字段

有些表格整理工作,说难也不难,说简单也不简单。

比如一批政府网站年报、企业年报、项目验收报告、审计报告或者统计年鉴 PDF,里面的字段都差不多,但每次都要人工打开文件、翻页、复制表格、整理 Excel。

文件少的时候还能忍,文件一多就很麻烦。

尤其是这种场景:

河北省-130000-2022.pdf
山西省-140000-2022.pdf
辽宁省-210000-2022.pdf
江苏省-320000-2022.pdf
浙江省-330000-2022.pdf

每份 PDF 里都有类似的表格字段:

  • • 网站名称
  • • 主办单位
  • • 网站类型
  • • 信息发布总数
  • • 政务动态信息数
  • • 解读回应信息数
  • • 办事服务事项数
  • • 互动交流留言数

人工处理时,一份 PDF 可能只需要几分钟,但几十份、几百份放在一起,就会变成非常重复的机械劳动。

这篇教程要做的事情很直接:

用 Python 批量读取 PDF 表格,自动提取核心字段,最后汇总成一张 Excel 表。


一、背景

很多 PDF 年报看起来像“表格”,但实际上并不是 Excel 表。

它们通常是 PDF 页面里的文字、线条和坐标信息。

人工可以直接看懂:

字段
内容
网站名称
某某省人民政府门户网站
主办单位
某某省人民政府办公厅
信息发布总数
12876
政务动态信息数
5632

但对程序来说,需要先把 PDF 页面里的表格结构识别出来,再把字段和字段值对应起来。

所以这个任务可以拆成四步:

读取 PDF 文件
→ 提取页面表格
→ 根据关键词定位字段
→ 汇总导出 Excel

最终我们希望得到一张这样的结果表:

省份
行政区划代码
年份
网站名称
主办单位
信息发布总数
政务动态信息数
河北省
130000
2022
河北省人民政府门户网站
河北省人民政府办公厅
12876
5632
山西省
140000
2022
山西省人民政府门户网站
山西省人民政府办公厅
10451
4819
江苏省
320000
2022
江苏省人民政府门户网站
江苏省人民政府办公厅
23560
9288

二、代码思路

这篇教程主要使用三个 Python 包:

工具
作用
pdfplumber
读取 PDF 页面并提取表格
pandas
整理表格数据并导出 Excel
re
从文件名中提取省份、代码和年份

整体代码逻辑如下:

1. 扫描 input_pdfs 文件夹中的所有 PDF
2. 从文件名中提取省份、行政区划代码、年份
3. 逐页读取 PDF 表格
4. 把表格内容拉平成文本单元格
5. 根据关键词查找字段值
6. 将每份 PDF 的结果合并
7. 导出 CSV 和 Excel 文件

三、安装依赖

先安装需要用到的 Python 包:

pip install pdfplumber pandas openpyxl

如果后面需要处理扫描版 PDF,还可以额外使用 OCR 工具。

但这次先处理最常见的一类:

带文本层的 PDF,也就是可以直接复制文字的 PDF。


四、核心代码

1. 导入依赖

import re
from
 pathlib import Path

import
 pandas as pd
import
 pdfplumber

这里用到的几个库分别负责:

  • • Path:管理文件路径;
  • • re:用正则表达式解析文件名;
  • • pandas:整理和导出结果;
  • • pdfplumber:读取 PDF 表格。

2. 从文件名提取信息

假设 PDF 文件名格式为:

河北省-130000-2022.pdf

我们可以从中提取:

  • • 省份:河北省
  • • 行政区划代码:130000
  • • 年份:2022

代码如下:

def parse_filename(pdf_path: Path) -> dict:
    """
    从文件名中提取省份、行政区划代码和年份。
    文件名示例:河北省-130000-2022.pdf
    """

    name = pdf_path.stem

    pattern = r"(.+?)-(\d{6})-(\d{4})"
    match
 = re.search(pattern, name)

    if
 not match:
        return
 {
            "省份"
: "",
            "行政区划代码"
: "",
            "年份"
: ""
        }

    province, code, year = match.groups()

    return
 {
        "省份"
: province,
        "行政区划代码"
: code,
        "年份"
: year
    }

这一步的作用是先把文件本身的信息提取出来。

很多年报 PDF 的文件名里已经包含地区和年份,这些信息不需要再从正文里找,直接从文件名解析更稳定。


3. 清洗单元格文本

PDF 表格提取出来以后,单元格里经常会有换行、空格、特殊符号。

所以先写一个文本清洗函数。

def clean_text(x):
    """
    清洗 PDF 表格中的单元格文本。
    """

    if
 x is None:
        return
 ""

    x = str(x)
    x = x.replace("\n", "")
    x = x.replace("\r", "")
    x = re.sub(r"\s+", "", x)

    return
 x.strip()

比如原始内容可能是:

信息发布
总数

清洗后会变成:

信息发布总数

这样后面用关键词匹配时会更方便。


4. 提取 PDF 中的所有表格

接下来读取 PDF 的每一页,并提取页面中的表格。

def extract_all_tables(pdf_path: Path) -> list:
    """
    提取 PDF 中的所有表格。
    返回结果是一个二维表格列表。
    """

    all_tables = []

    with
 pdfplumber.open(pdf_path) as pdf:
        for
 page_no, page in enumerate(pdf.pages, start=1):
            tables = page.extract_tables()

            for
 table in tables:
                cleaned_table = []
                for
 row in table:
                    cleaned_row = [clean_text(cell) for cell in row]
                    cleaned_table.append(cleaned_row)

                all_tables.append({
                    "page"
: page_no,
                    "table"
: cleaned_table
                })

    return
 all_tables

这里需要注意:

  • • 一个 PDF 可能有很多页;
  • • 一页里可能有多个表格;
  • • 每个表格可能有很多行和列;
  • • 提取后要统一清洗单元格内容。

5. 根据关键词查找字段值

年报表格通常是“字段名—字段值”的形式。

比如:

字段
网站名称
河北省人民政府门户网站
主办单位
河北省人民政府办公厅
信息发布总数
12876

我们可以根据关键词找到字段所在单元格,然后取右侧单元格作为字段值。

def find_value_by_keyword(all_tables: list, keyword: str) -> str:
    """
    在所有表格中查找关键词,并返回其右侧单元格内容。
    """

    for
 item in all_tables:
        table = item["table"]

        for
 row in table:
            for
 col_idx, cell in enumerate(row):
                if
 keyword in cell:
                    if
 col_idx + 1 < len(row):
                        value = row[col_idx + 1]
                        if
 value:
                            return
 value

                    cell_clean = cell.replace(keyword, "")
                    if
 cell_clean:
                        return
 cell_clean

    return
 ""

这个函数的核心逻辑是:

找到关键词
→ 查看右侧单元格
→ 如果右侧有值,就返回
→ 如果没有,再尝试从当前单元格中拆出值

6. 提取单个 PDF 的核心字段

现在可以把前面的函数组合起来,提取一份 PDF 的关键信息。

def extract_one_pdf(pdf_path: Path) -> dict:
    """
    提取单个 PDF 年报中的核心字段。
    """

    file_info = parse_filename(pdf_path)
    all_tables = extract_all_tables(pdf_path)

    field_map = {
        "网站名称"
: "网站名称",
        "主办单位"
: "主办单位",
        "网站类型"
: "网站类型",
        "信息发布总数"
: "信息发布总数",
        "政务动态信息数"
: "政务动态信息数",
        "解读回应信息数"
: "解读回应信息数",
        "办事服务事项数"
: "办事服务事项数",
        "互动交流留言数"
: "互动交流留言数"
    }

    result = {
        "文件名"
: pdf_path.name,
        **file_info
    }

    for
 output_col, keyword in field_map.items():
        result[output_col] = find_value_by_keyword(all_tables, keyword)

    return
 result

这里的 field_map 可以按照自己的 PDF 内容继续扩展。

比如你想提取更多字段,可以继续加:

field_map = {
    "网站名称"
: "网站名称",
    "主办单位"
: "主办单位",
    "年度总访问量"
: "年度总访问量",
    "首页访问量"
: "首页访问量",
    "新媒体账号数量"
: "新媒体账号数量"
}

7. 批量处理文件夹

最后写一个批量处理函数,扫描整个文件夹里的 PDF。

def batch_extract(input_dir: str, output_dir: str):
    """
    批量提取 PDF 表格字段,并导出 CSV 和 Excel。
    """

    input_path = Path(input_dir)
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)

    pdf_files = sorted(input_path.glob("*.pdf"))

    records = []

    for
 pdf_file in pdf_files:
        print
(f"正在处理:{pdf_file.name}")

        try
:
            record = extract_one_pdf(pdf_file)
            records.append(record)
        except
 Exception as e:
            records.append({
                "文件名"
: pdf_file.name,
                "错误信息"
: str(e)
            })

    df = pd.DataFrame(records)

    csv_path = output_path / "pdf_year_report_summary.csv"
    xlsx_path = output_path / "pdf_year_report_summary.xlsx"

    df.to_csv(csv_path, index=False, encoding="utf-8-sig")
    df.to_excel(xlsx_path, index=False)

    print
("处理完成")
    print
(f"CSV 文件已保存:{csv_path}")
    print
(f"Excel 文件已保存:{xlsx_path}")

这个函数做了几件事:

  1. 1. 找到所有 PDF 文件;
  2. 2. 逐个 PDF 提取字段;
  3. 3. 如果某个文件报错,也不会中断整个程序;
  4. 4. 最后统一导出 CSV 和 Excel。

8. 主程序入口

完整脚本最后加上主程序入口:

if __name__ == "__main__":
    batch_extract(
        input_dir="input_pdfs",
        output_dir="output"
    )

运行代码:

python extract_pdf_tables.py

五、结果展示

代码运行后,会在 output 文件夹中生成两个文件:

pdf_year_report_summary.csv
pdf_year_report_summary.xlsx

汇总结果示例如下:

文件名
省份
行政区划代码
年份
网站名称
主办单位
信息发布总数
政务动态信息数
河北省-130000-2022.pdf
河北省
130000
2022
河北省人民政府门户网站
河北省人民政府办公厅
12876
5632
山西省-140000-2022.pdf
山西省
140000
2022
山西省人民政府门户网站
山西省人民政府办公厅
10451
4819
江苏省-320000-2022.pdf
江苏省
320000
2022
江苏省人民政府门户网站
江苏省人民政府办公厅
23560
9288

从结果可以看到,原本分散在多个 PDF 里的字段已经被统一整理到一张 Excel 表中。

这样后续无论是做描述统计、地区对比,还是进一步合并到面板数据里,都可以直接使用。


六、结果解读

其实重点不是单纯“读取 PDF”,而是完成一条完整的数据整理流程:

批量 PDF 文件
→ 自动识别文件信息
→ 提取表格字段
→ 汇总结构化数据
→ 导出 Excel / CSV

相比人工整理,这种方法有三个优势。

1. 节省重复劳动

人工需要逐份 PDF 打开、复制、粘贴。

代码只需要把 PDF 放进指定文件夹,一次运行即可批量处理。

2. 字段格式统一

人工整理时,容易出现字段命名不一致的问题。

比如:

信息发布总数
信息发布数量
发布信息总数

代码可以通过统一字段表进行规范化处理。

3. 方便后续分析

导出的 Excel 表可以继续用于:

  • • 地区对比;
  • • 年份对比;
  • • 政务公开水平测度;
  • • 政府网站绩效评价;
  • • 政策执行透明度指标构建。

七、总结

这次我们演示了如何用 Python 批量提取 PDF 年报中的表格字段。

整个流程可以概括为:

读取 PDF
→ 提取表格
→ 匹配关键词
→ 整理字段
→ 导出 Excel

对于大量格式相近的 PDF 文件,这种方法非常适合用来减少重复劳动。

它尤其适用于:

  • • 政府网站年报整理;
  • • 企业年报字段提取;
  • • 项目验收报告汇总;
  • • 审计报告表格提取;
  • • 统计公报数据整理;
  • • 政策文件附表提取。

最后得到的不是一堆零散 PDF,而是一张可以继续分析的结构化数据表。

这也是 Python 自动化处理 PDF 的核心价值:

把“看得见但不好用”的 PDF 表格,变成“能统计、能合并、能分析”的 Excel 数据。