1. 项目概述从海量PDF中挖掘关键信息在金融、法律、咨询或任何涉及大量文档研究的领域分析师们常常面临一个既基础又繁琐的任务从成百上千份PDF格式的上市公司年报、招股说明书、法律文件中快速定位并统计特定关键词的出现频率。手动打开每一份文档用CtrlF逐个搜索不仅效率低下而且极易出错和遗漏。这正是“批量处理PDF文档并统计关键词”这个需求的核心痛点。我最近就接手了一个类似的项目需要分析某行业近五年所有上市公司的年报追踪“数字化转型”、“研发投入”、“碳中和”等战略关键词的提及趋势。如果靠人力这几乎是一个不可能完成的任务。但借助Python我们完全可以构建一个自动化流水线让计算机不知疲倦地完成这份“苦力活”。最终的目标很明确输入一堆PDF文件和一个自定义关键词列表程序能自动输出一份清晰的报告告诉我们每个关键词在每份文档中出现了多少次。这不仅仅是简单的文本查找。上市公司文档结构复杂包含表格、图表、页眉页脚文本提取的准确性直接决定了统计结果的可靠性。此外关键词的匹配也需要考虑大小写、单复数、近义词等实际情况。接下来我就把这次实战中搭建的完整解决方案、踩过的坑以及提升效率的技巧毫无保留地分享出来。2. 核心工具链选型与搭建工欲善其事必先利其器。处理PDF的Python库有很多但各有优劣选对工具能避免后续无数麻烦。2.1 PDF文本提取库PyMuPDF vs. pdfplumber这是最核心的一环。经过反复测试我主要推荐两个库PyMuPDF (fitz)这是一个功能强大、速度极快的库。它不仅能提取文本还能处理文档结构、获取元数据、甚至渲染页面。对于以文字为主、格式相对规范的上市公司文档如年报正文它的提取速度和准确率都非常高。它的文本提取可以保留一定的位置信息对于区分正文和页脚很有帮助。import fitz # PyMuPDF def extract_text_with_fitz(pdf_path): doc fitz.open(pdf_path) text for page in doc: text page.get_text() doc.close() return textpdfplumber这个库的强大之处在于它能更精细地定位页面上的每个字符、线条和矩形框。这对于从PDF表格中提取数据是无价之宝。许多年报中的关键数据如财务报表摘要是以表格形式呈现的pdfplumber可以相对准确地将表格还原为结构化的数据如列表的列表这对于统计表格内的关键词至关重要。import pdfplumber def extract_text_and_tables_with_pdfplumber(pdf_path): text tables_data [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text page.extract_text() # 尝试提取当前页的所有表格 page_tables page.extract_tables() tables_data.extend(page_tables) return text, tables_data我的选择心得在实际项目中我采用了混合策略。首先用PyMuPDF快速提取全部文本进行初步的词频统计。然后对于已知包含重要数据表格的页面如“合并利润表”再用pdfplumber进行二次精确提取和分析。这样兼顾了整体效率和关键局部的准确性。2.2 文本处理与关键词匹配库提取出纯文本后我们需要对其进行清洗和搜索。正则表达式 (re)这是进行灵活、强大文本匹配的基石。不仅仅是简单的字符串查找我们可以用正则来处理关键词的变体。例如搜索“AI”我们可能也希望匹配“A.I.”、“人工智能”在中文文档中。正则表达式能定义复杂的模式。字符串方法 (str.lower(), str.count())对于简单的、精确的、不区分大小写的匹配直接使用字符串的lower()和count()方法可能比正则更快。自然语言处理库如 jieba, nltk对于更高级的需求例如需要识别关键词在上下文中的具体含义区分“苹果”公司和“苹果”水果或者进行近义词扩展就需要引入NLP工具。在中文场景下jieba分词是预处理的关键步骤。2.3 环境搭建与依赖管理创建一个独立的项目环境是专业工作的好习惯。我强烈推荐使用conda或venv创建虚拟环境并使用requirements.txt管理依赖。创建虚拟环境# 使用 conda conda create -n pdf_keyword_analysis python3.9 conda activate pdf_keyword_analysis # 或使用 venv python -m venv venv # Windows .\venv\Scripts\activate # Linux/Mac source venv/bin/activate安装核心库pip install pymupdf pdfplumber pandaspandas并非必须但它对于最终结果的整理、分析和导出为Excel/CSV格式极其方便。生成依赖文件项目完成后运行pip freeze requirements.txt方便在任何地方复现环境。3. 实战步骤拆解从PDF到统计报告下面我将整个流程拆解为可顺序执行的步骤并附上详细的代码和解释。3.1 第一步规划输入与输出在写代码之前先明确接口。我设计了一个简单的文件夹结构project/ ├── input_pdfs/ # 存放所有待分析的PDF文件 ├── keywords.txt # 每行一个关键词 ├── main.py # 主程序 └── results/ # 程序输出的结果目录输出我希望是一个CSV文件行是每个PDF文件名列是每个关键词交叉的单元格就是该关键词在该文件中的出现次数。另外最好再生成一个每个文件的详细日志记录提取状态和可能的问题。3.2 第二步批量读取PDF与文本提取这里的关键是健壮性。不是每个PDF都能被完美读取所以必须有异常处理。import os import fitz import pdfplumber from typing import List, Tuple def extract_text_from_pdf(pdf_path: str, strategy: str fitz) - Tuple[str, List]: 从PDF提取文本和表格。 :param pdf_path: PDF文件路径 :param strategy: 提取策略fitz 或 plumber :return: (纯文本, 表格数据列表) full_text tables [] try: if strategy fitz: doc fitz.open(pdf_path) for page_num, page in enumerate(doc): # 提取文本 page_text page.get_text() full_text page_text \n # 添加换行分隔页面 doc.close() elif strategy plumber: with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: page_text page.extract_text() or # 防止返回None full_text page_text \n # 提取表格 page_tables page.extract_tables() if page_tables: for table in page_tables: tables.append(table) # table是一个二维列表 else: raise ValueError(不支持的提取策略) except Exception as e: print(f警告处理文件 {pdf_path} 时出错: {e}) # 返回空结果但记录错误避免整个程序崩溃 return , [] return full_text, tables def batch_extract(pdf_dir: str) - dict: 批量提取一个文件夹下所有PDF的文本。 :param pdf_dir: PDF文件夹路径 :return: 字典键为文件名值为(文本, 表格)元组 pdf_files [f for f in os.listdir(pdf_dir) if f.lower().endswith(.pdf)] all_data {} for pdf_file in pdf_files: pdf_path os.path.join(pdf_dir, pdf_file) print(f正在处理: {pdf_file}) # 默认使用fitz速度快。对于特定文件可以后续用plumber二次处理。 text, tables extract_text_from_pdf(pdf_path, strategyfitz) all_data[pdf_file] (text, tables) return all_data注意pdfplumber的extract_tables()并不总是完美对于复杂的、有合并单元格的表格提取结果可能需要人工校对或后处理。在自动化流程中我通常将表格数据单独保存供后续专项分析而在关键词初筛时更依赖纯文本。3.3 第三步加载关键词与设计匹配逻辑关键词列表可以从文本文件加载方便非技术人员修改。def load_keywords(keyword_file_path: str) - List[str]: 从文本文件加载关键词每行一个忽略空行和注释 keywords [] with open(keyword_file_path, r, encodingutf-8) as f: for line in f: line line.strip() if line and not line.startswith(#): # 支持用#注释 keywords.append(line) return keywords匹配逻辑是核心。简单的计数可以用str.count()但为了更灵活我通常使用正则表达式并实现一个“增强匹配”函数。import re def enhanced_keyword_count(text: str, keyword: str) - int: 增强型关键词计数。 1. 不区分大小写。 2. 处理关键词中的特殊正则字符。 3. 可以考虑匹配单词边界\\b但中文不适用。 # 转义关键词中的特殊正则字符如 [、]、*、 等 escaped_keyword re.escape(keyword) # 构建正则模式不区分大小写并考虑中文上下文不使用\\b # 这里使用 (?i) 标志表示忽略大小写 pattern re.compile(f(?i){escaped_keyword}) matches pattern.findall(text) return len(matches) # 对于中文或需要处理空格的情况可以更精细地定义模式 def count_keyword_in_text(text, keyword): 一个更基础的版本直接使用字符串方法。 适用于简单、精确的匹配需求速度更快。 # 将文本和关键词都转为小写进行不区分大小写的匹配 lower_text text.lower() lower_keyword keyword.lower() return lower_text.count(lower_keyword)3.4 第四步执行批量统计与汇总结果现在将前面几步串联起来。我们遍历每个PDF的提取文本对每个关键词进行计数并将结果存入pandas DataFrame。import pandas as pd def analyze_pdfs(pdf_data_dict: dict, keywords: List[str]) - pd.DataFrame: 分析所有PDF数据统计关键词出现次数。 :param pdf_data_dict: 由 batch_extract 返回的字典 :param keywords: 关键词列表 :return: 包含统计结果的DataFrame # 初始化结果字典 results [] for pdf_name, (text, tables) in pdf_data_dict.items(): print(f正在分析: {pdf_name}) file_result {文件名: pdf_name} total_words len(text) # 可选记录文档总字数用于计算词频密度 for kw in keywords: # 使用增强计数函数 count enhanced_keyword_count(text, kw) # 如果需要也可以搜索表格中的文本将表格展平为字符串 table_text_count 0 for table in tables: for row in table: for cell in row: if cell and isinstance(cell, str): table_text_count enhanced_keyword_count(cell, kw) total_count count table_text_count file_result[kw] total_count file_result[文档总字符数] total_words # 可选字段 results.append(file_result) # 转换为DataFrame df_results pd.DataFrame(results) # 将文件名设为索引可选 df_results.set_index(文件名, inplaceTrue) return df_results3.5 第五步结果导出与可视化得到DataFrame后我们可以轻松地导出为各种格式并进行初步的可视化。def export_results(df: pd.DataFrame, output_dir: str): 导出结果到CSV和Excel并生成简单图表。 os.makedirs(output_dir, exist_okTrue) csv_path os.path.join(output_dir, keyword_counts.csv) excel_path os.path.join(output_dir, keyword_counts.xlsx) # 导出到CSV df.to_csv(csv_path, encodingutf-8-sig) # utf-8-sig支持Excel中文 print(f结果已保存至 CSV: {csv_path}) # 导出到Excel (需要 openpyxl 或 xlsxwriter) try: df.to_excel(excel_path, sheet_name关键词统计) print(f结果已保存至 Excel: {excel_path}) except Exception as e: print(f导出Excel失败请安装openpyxl: pip install openpyxl. 错误: {e}) # 简单可视化例如找出提及最多的关键词跨文档求和 if not df.empty: keyword_totals df.sum() # 对每列关键词求和 # 排除非数值列如“文档总字符数” numeric_cols df.select_dtypes(include[number]).columns keyword_totals df[numeric_cols].sum() # 可以在这里使用 matplotlib 或 seaborn 绘图 # import matplotlib.pyplot as plt # keyword_totals.sort_values(ascendingFalse).head(10).plot(kindbarh) # plt.title(Top 10 关键词总出现次数) # plt.tight_layout() # plt.savefig(os.path.join(output_dir, top_keywords.png)) # plt.show() # 将汇总结果也保存下来 totals_df keyword_totals.to_frame(name总出现次数).sort_values(总出现次数, ascendingFalse) totals_df.to_csv(os.path.join(output_dir, keyword_totals_summary.csv), encodingutf-8-sig)3.6 第六步主程序整合最后创建一个主函数来协调整个流程。def main(pdf_folder, keyword_file, output_folder): 主执行函数 print( PDF关键词批量分析程序开始 ) # 1. 加载关键词 print(步骤1/4: 加载关键词...) keywords load_keywords(keyword_file) print(f已加载 {len(keywords)} 个关键词: {keywords}) # 2. 批量提取PDF文本 print(f\n步骤2/4: 从 {pdf_folder} 批量提取PDF文本...) pdf_data batch_extract(pdf_folder) print(f成功处理 {len(pdf_data)} 个PDF文件。) # 3. 执行关键词统计 print(f\n步骤3/4: 执行关键词统计...) results_df analyze_pdfs(pdf_data, keywords) # 4. 导出结果 print(f\n步骤4/4: 导出结果到 {output_folder} ...) export_results(results_df, output_folder) print(\n 分析完成 ) print(f详细结果请查看 {output_folder} 目录。) if __name__ __main__: # 配置你的路径 PDF_INPUT_FOLDER ./input_pdfs KEYWORD_FILE ./keywords.txt OUTPUT_FOLDER ./results main(PDF_INPUT_FOLDER, KEYWORD_FILE, OUTPUT_FOLDER)4. 高级技巧与常见问题排查在实际操作中你会遇到各种各样的问题。下面是我总结的一些进阶技巧和避坑指南。4.1 提升文本提取质量的技巧处理扫描版PDF图片型PDF如果PDF是扫描图片生成的上述所有方法都会失效因为里面没有可提取的文本。这时必须使用OCR光学字符识别。pytesseract库结合pdf2image将PDF页面转为图片是常用方案但速度会慢很多。from pdf2image import convert_from_path import pytesseract def ocr_pdf(pdf_path): images convert_from_path(pdf_path) full_text for image in images: text pytesseract.image_to_string(image, langchi_simeng) # 中英文识别 full_text text \n return full_text注意OCR准确率受图片质量影响极大且需要单独安装Tesseract-OCR引擎。对于大批量、高精度要求的商业分析可能需要采购更专业的OCR服务。处理加密PDF如果PDF有密码保护PyMuPDF在打开时需要提供密码fitz.open(pdf_path, passwordyour_password)。批量处理时可以将密码记录在一个配置文件中。清理提取的文本提取的文本常包含多余的空格、换行符和乱码。在统计前进行清洗很重要。import re def clean_text(text): # 合并多个空格和换行符为一个空格 text re.sub(r\s, , text) # 移除不可见或特殊字符根据实际情况调整 text re.sub(r[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f], , text) return text.strip()4.2 优化关键词匹配策略近义词与同义词扩展单纯匹配“人工智能”可能会漏掉“AI”、“智能技术”、“机器学习”等表述。可以预先构建一个同义词词典。synonym_dict { 人工智能: [AI, A.I., 智能算法, 机器学习], 碳中和: [碳达峰, 净零排放, 碳抵消], } # 在统计时将一个主关键词的所有同义词出现次数相加处理否定语境统计“出现次数”有时会误导比如“我们不采用区块链技术”。简单的词频统计无法区分。这需要更复杂的NLP情感分析或上下文判断超出了本基础项目的范围但在做结论时需要保持警惕。使用更高效的计数方法如果文档量巨大上万份关键词也很多纯Python循环可能较慢。可以考虑使用multiprocessing库进行多进程并行处理充分利用多核CPU。对于超大规模文本可以考虑将文本向量化后使用更高效的搜索算法但这属于高级优化范畴。4.3 常见错误与解决方案实录在运行上述代码时你很可能遇到以下问题问题现象可能原因解决方案ModuleNotFoundError: No module named fitz未正确安装PyMuPDFpip install PyMuPDF。注意导入时是import fitz但包名是PyMuPDF。pdfplumber提取表格为空或错乱PDF表格结构复杂或本质上是图片1. 尝试调整pdfplumber的extract_tables()参数如vertical_strategy,horizontal_strategy。2. 确认该页面是否为图片是则需OCR。关键词计数远低于/高于预期1. 文本提取不完整如漏了页眉。2. 匹配逻辑问题大小写、单词边界。3. 文档是扫描件。1. 打印几页提取的文本肉眼核对。2. 检查enhanced_keyword_count函数调试单个关键词的匹配。3. 用PDF阅读器检查文档属性看是否是“仅图像”。程序处理大量PDF时内存不足一次性将所有PDF内容加载到内存采用流式处理处理完一个PDF立即保存结果并释放内存再处理下一个。避免在pdf_data_dict中堆积所有原始文本。中文关键词匹配不到文本提取时编码问题或PDF字体特殊1. 确保所有文件操作读关键词、写结果使用utf-8编码。2. 尝试用pdfplumber并指定laparams参数改善中文布局分析page.extract_text(laparams{line_overlap: 0.7})pytesseract找不到Tesseract未安装Tesseract-OCR或路径不对1. 从GitHub安装Tesseract。2. 在代码中指定路径pytesseract.pytesseract.tesseract_cmd rC:\Program Files\Tesseract-OCR\tesseract.exe4.4 性能优化与扩展思路当项目规模扩大时以下几点可以帮助你增量处理如果每天都有新PDF加入可以设计一个机制只处理新的或修改过的文件。记录已处理文件的MD5哈希值或最后修改时间。结果数据库存储当结果数据量很大时将结果存入SQLite或MySQL数据库比CSV文件更便于查询和历史对比分析。添加日志系统使用Python的logging模块替代print可以更规范地记录信息、警告和错误方便后期排查。构建Web界面或自动化脚本使用Flask或Streamlit快速搭建一个上传PDF、输入关键词、查看结果的可视化界面交付给非技术同事使用。或者将主程序包装成定时任务如使用cron或APScheduler实现全自动化运行。这个项目虽然起点是一个简单的“关键词计数”但其内核是一个灵活的文档信息提取框架。掌握了它你就能应对许多类似的自动化文本处理需求从海量文档中解放双手让数据自己说话。
Python批量处理PDF文档:自动化关键词统计与信息提取实战
1. 项目概述从海量PDF中挖掘关键信息在金融、法律、咨询或任何涉及大量文档研究的领域分析师们常常面临一个既基础又繁琐的任务从成百上千份PDF格式的上市公司年报、招股说明书、法律文件中快速定位并统计特定关键词的出现频率。手动打开每一份文档用CtrlF逐个搜索不仅效率低下而且极易出错和遗漏。这正是“批量处理PDF文档并统计关键词”这个需求的核心痛点。我最近就接手了一个类似的项目需要分析某行业近五年所有上市公司的年报追踪“数字化转型”、“研发投入”、“碳中和”等战略关键词的提及趋势。如果靠人力这几乎是一个不可能完成的任务。但借助Python我们完全可以构建一个自动化流水线让计算机不知疲倦地完成这份“苦力活”。最终的目标很明确输入一堆PDF文件和一个自定义关键词列表程序能自动输出一份清晰的报告告诉我们每个关键词在每份文档中出现了多少次。这不仅仅是简单的文本查找。上市公司文档结构复杂包含表格、图表、页眉页脚文本提取的准确性直接决定了统计结果的可靠性。此外关键词的匹配也需要考虑大小写、单复数、近义词等实际情况。接下来我就把这次实战中搭建的完整解决方案、踩过的坑以及提升效率的技巧毫无保留地分享出来。2. 核心工具链选型与搭建工欲善其事必先利其器。处理PDF的Python库有很多但各有优劣选对工具能避免后续无数麻烦。2.1 PDF文本提取库PyMuPDF vs. pdfplumber这是最核心的一环。经过反复测试我主要推荐两个库PyMuPDF (fitz)这是一个功能强大、速度极快的库。它不仅能提取文本还能处理文档结构、获取元数据、甚至渲染页面。对于以文字为主、格式相对规范的上市公司文档如年报正文它的提取速度和准确率都非常高。它的文本提取可以保留一定的位置信息对于区分正文和页脚很有帮助。import fitz # PyMuPDF def extract_text_with_fitz(pdf_path): doc fitz.open(pdf_path) text for page in doc: text page.get_text() doc.close() return textpdfplumber这个库的强大之处在于它能更精细地定位页面上的每个字符、线条和矩形框。这对于从PDF表格中提取数据是无价之宝。许多年报中的关键数据如财务报表摘要是以表格形式呈现的pdfplumber可以相对准确地将表格还原为结构化的数据如列表的列表这对于统计表格内的关键词至关重要。import pdfplumber def extract_text_and_tables_with_pdfplumber(pdf_path): text tables_data [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text page.extract_text() # 尝试提取当前页的所有表格 page_tables page.extract_tables() tables_data.extend(page_tables) return text, tables_data我的选择心得在实际项目中我采用了混合策略。首先用PyMuPDF快速提取全部文本进行初步的词频统计。然后对于已知包含重要数据表格的页面如“合并利润表”再用pdfplumber进行二次精确提取和分析。这样兼顾了整体效率和关键局部的准确性。2.2 文本处理与关键词匹配库提取出纯文本后我们需要对其进行清洗和搜索。正则表达式 (re)这是进行灵活、强大文本匹配的基石。不仅仅是简单的字符串查找我们可以用正则来处理关键词的变体。例如搜索“AI”我们可能也希望匹配“A.I.”、“人工智能”在中文文档中。正则表达式能定义复杂的模式。字符串方法 (str.lower(), str.count())对于简单的、精确的、不区分大小写的匹配直接使用字符串的lower()和count()方法可能比正则更快。自然语言处理库如 jieba, nltk对于更高级的需求例如需要识别关键词在上下文中的具体含义区分“苹果”公司和“苹果”水果或者进行近义词扩展就需要引入NLP工具。在中文场景下jieba分词是预处理的关键步骤。2.3 环境搭建与依赖管理创建一个独立的项目环境是专业工作的好习惯。我强烈推荐使用conda或venv创建虚拟环境并使用requirements.txt管理依赖。创建虚拟环境# 使用 conda conda create -n pdf_keyword_analysis python3.9 conda activate pdf_keyword_analysis # 或使用 venv python -m venv venv # Windows .\venv\Scripts\activate # Linux/Mac source venv/bin/activate安装核心库pip install pymupdf pdfplumber pandaspandas并非必须但它对于最终结果的整理、分析和导出为Excel/CSV格式极其方便。生成依赖文件项目完成后运行pip freeze requirements.txt方便在任何地方复现环境。3. 实战步骤拆解从PDF到统计报告下面我将整个流程拆解为可顺序执行的步骤并附上详细的代码和解释。3.1 第一步规划输入与输出在写代码之前先明确接口。我设计了一个简单的文件夹结构project/ ├── input_pdfs/ # 存放所有待分析的PDF文件 ├── keywords.txt # 每行一个关键词 ├── main.py # 主程序 └── results/ # 程序输出的结果目录输出我希望是一个CSV文件行是每个PDF文件名列是每个关键词交叉的单元格就是该关键词在该文件中的出现次数。另外最好再生成一个每个文件的详细日志记录提取状态和可能的问题。3.2 第二步批量读取PDF与文本提取这里的关键是健壮性。不是每个PDF都能被完美读取所以必须有异常处理。import os import fitz import pdfplumber from typing import List, Tuple def extract_text_from_pdf(pdf_path: str, strategy: str fitz) - Tuple[str, List]: 从PDF提取文本和表格。 :param pdf_path: PDF文件路径 :param strategy: 提取策略fitz 或 plumber :return: (纯文本, 表格数据列表) full_text tables [] try: if strategy fitz: doc fitz.open(pdf_path) for page_num, page in enumerate(doc): # 提取文本 page_text page.get_text() full_text page_text \n # 添加换行分隔页面 doc.close() elif strategy plumber: with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: page_text page.extract_text() or # 防止返回None full_text page_text \n # 提取表格 page_tables page.extract_tables() if page_tables: for table in page_tables: tables.append(table) # table是一个二维列表 else: raise ValueError(不支持的提取策略) except Exception as e: print(f警告处理文件 {pdf_path} 时出错: {e}) # 返回空结果但记录错误避免整个程序崩溃 return , [] return full_text, tables def batch_extract(pdf_dir: str) - dict: 批量提取一个文件夹下所有PDF的文本。 :param pdf_dir: PDF文件夹路径 :return: 字典键为文件名值为(文本, 表格)元组 pdf_files [f for f in os.listdir(pdf_dir) if f.lower().endswith(.pdf)] all_data {} for pdf_file in pdf_files: pdf_path os.path.join(pdf_dir, pdf_file) print(f正在处理: {pdf_file}) # 默认使用fitz速度快。对于特定文件可以后续用plumber二次处理。 text, tables extract_text_from_pdf(pdf_path, strategyfitz) all_data[pdf_file] (text, tables) return all_data注意pdfplumber的extract_tables()并不总是完美对于复杂的、有合并单元格的表格提取结果可能需要人工校对或后处理。在自动化流程中我通常将表格数据单独保存供后续专项分析而在关键词初筛时更依赖纯文本。3.3 第三步加载关键词与设计匹配逻辑关键词列表可以从文本文件加载方便非技术人员修改。def load_keywords(keyword_file_path: str) - List[str]: 从文本文件加载关键词每行一个忽略空行和注释 keywords [] with open(keyword_file_path, r, encodingutf-8) as f: for line in f: line line.strip() if line and not line.startswith(#): # 支持用#注释 keywords.append(line) return keywords匹配逻辑是核心。简单的计数可以用str.count()但为了更灵活我通常使用正则表达式并实现一个“增强匹配”函数。import re def enhanced_keyword_count(text: str, keyword: str) - int: 增强型关键词计数。 1. 不区分大小写。 2. 处理关键词中的特殊正则字符。 3. 可以考虑匹配单词边界\\b但中文不适用。 # 转义关键词中的特殊正则字符如 [、]、*、 等 escaped_keyword re.escape(keyword) # 构建正则模式不区分大小写并考虑中文上下文不使用\\b # 这里使用 (?i) 标志表示忽略大小写 pattern re.compile(f(?i){escaped_keyword}) matches pattern.findall(text) return len(matches) # 对于中文或需要处理空格的情况可以更精细地定义模式 def count_keyword_in_text(text, keyword): 一个更基础的版本直接使用字符串方法。 适用于简单、精确的匹配需求速度更快。 # 将文本和关键词都转为小写进行不区分大小写的匹配 lower_text text.lower() lower_keyword keyword.lower() return lower_text.count(lower_keyword)3.4 第四步执行批量统计与汇总结果现在将前面几步串联起来。我们遍历每个PDF的提取文本对每个关键词进行计数并将结果存入pandas DataFrame。import pandas as pd def analyze_pdfs(pdf_data_dict: dict, keywords: List[str]) - pd.DataFrame: 分析所有PDF数据统计关键词出现次数。 :param pdf_data_dict: 由 batch_extract 返回的字典 :param keywords: 关键词列表 :return: 包含统计结果的DataFrame # 初始化结果字典 results [] for pdf_name, (text, tables) in pdf_data_dict.items(): print(f正在分析: {pdf_name}) file_result {文件名: pdf_name} total_words len(text) # 可选记录文档总字数用于计算词频密度 for kw in keywords: # 使用增强计数函数 count enhanced_keyword_count(text, kw) # 如果需要也可以搜索表格中的文本将表格展平为字符串 table_text_count 0 for table in tables: for row in table: for cell in row: if cell and isinstance(cell, str): table_text_count enhanced_keyword_count(cell, kw) total_count count table_text_count file_result[kw] total_count file_result[文档总字符数] total_words # 可选字段 results.append(file_result) # 转换为DataFrame df_results pd.DataFrame(results) # 将文件名设为索引可选 df_results.set_index(文件名, inplaceTrue) return df_results3.5 第五步结果导出与可视化得到DataFrame后我们可以轻松地导出为各种格式并进行初步的可视化。def export_results(df: pd.DataFrame, output_dir: str): 导出结果到CSV和Excel并生成简单图表。 os.makedirs(output_dir, exist_okTrue) csv_path os.path.join(output_dir, keyword_counts.csv) excel_path os.path.join(output_dir, keyword_counts.xlsx) # 导出到CSV df.to_csv(csv_path, encodingutf-8-sig) # utf-8-sig支持Excel中文 print(f结果已保存至 CSV: {csv_path}) # 导出到Excel (需要 openpyxl 或 xlsxwriter) try: df.to_excel(excel_path, sheet_name关键词统计) print(f结果已保存至 Excel: {excel_path}) except Exception as e: print(f导出Excel失败请安装openpyxl: pip install openpyxl. 错误: {e}) # 简单可视化例如找出提及最多的关键词跨文档求和 if not df.empty: keyword_totals df.sum() # 对每列关键词求和 # 排除非数值列如“文档总字符数” numeric_cols df.select_dtypes(include[number]).columns keyword_totals df[numeric_cols].sum() # 可以在这里使用 matplotlib 或 seaborn 绘图 # import matplotlib.pyplot as plt # keyword_totals.sort_values(ascendingFalse).head(10).plot(kindbarh) # plt.title(Top 10 关键词总出现次数) # plt.tight_layout() # plt.savefig(os.path.join(output_dir, top_keywords.png)) # plt.show() # 将汇总结果也保存下来 totals_df keyword_totals.to_frame(name总出现次数).sort_values(总出现次数, ascendingFalse) totals_df.to_csv(os.path.join(output_dir, keyword_totals_summary.csv), encodingutf-8-sig)3.6 第六步主程序整合最后创建一个主函数来协调整个流程。def main(pdf_folder, keyword_file, output_folder): 主执行函数 print( PDF关键词批量分析程序开始 ) # 1. 加载关键词 print(步骤1/4: 加载关键词...) keywords load_keywords(keyword_file) print(f已加载 {len(keywords)} 个关键词: {keywords}) # 2. 批量提取PDF文本 print(f\n步骤2/4: 从 {pdf_folder} 批量提取PDF文本...) pdf_data batch_extract(pdf_folder) print(f成功处理 {len(pdf_data)} 个PDF文件。) # 3. 执行关键词统计 print(f\n步骤3/4: 执行关键词统计...) results_df analyze_pdfs(pdf_data, keywords) # 4. 导出结果 print(f\n步骤4/4: 导出结果到 {output_folder} ...) export_results(results_df, output_folder) print(\n 分析完成 ) print(f详细结果请查看 {output_folder} 目录。) if __name__ __main__: # 配置你的路径 PDF_INPUT_FOLDER ./input_pdfs KEYWORD_FILE ./keywords.txt OUTPUT_FOLDER ./results main(PDF_INPUT_FOLDER, KEYWORD_FILE, OUTPUT_FOLDER)4. 高级技巧与常见问题排查在实际操作中你会遇到各种各样的问题。下面是我总结的一些进阶技巧和避坑指南。4.1 提升文本提取质量的技巧处理扫描版PDF图片型PDF如果PDF是扫描图片生成的上述所有方法都会失效因为里面没有可提取的文本。这时必须使用OCR光学字符识别。pytesseract库结合pdf2image将PDF页面转为图片是常用方案但速度会慢很多。from pdf2image import convert_from_path import pytesseract def ocr_pdf(pdf_path): images convert_from_path(pdf_path) full_text for image in images: text pytesseract.image_to_string(image, langchi_simeng) # 中英文识别 full_text text \n return full_text注意OCR准确率受图片质量影响极大且需要单独安装Tesseract-OCR引擎。对于大批量、高精度要求的商业分析可能需要采购更专业的OCR服务。处理加密PDF如果PDF有密码保护PyMuPDF在打开时需要提供密码fitz.open(pdf_path, passwordyour_password)。批量处理时可以将密码记录在一个配置文件中。清理提取的文本提取的文本常包含多余的空格、换行符和乱码。在统计前进行清洗很重要。import re def clean_text(text): # 合并多个空格和换行符为一个空格 text re.sub(r\s, , text) # 移除不可见或特殊字符根据实际情况调整 text re.sub(r[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f], , text) return text.strip()4.2 优化关键词匹配策略近义词与同义词扩展单纯匹配“人工智能”可能会漏掉“AI”、“智能技术”、“机器学习”等表述。可以预先构建一个同义词词典。synonym_dict { 人工智能: [AI, A.I., 智能算法, 机器学习], 碳中和: [碳达峰, 净零排放, 碳抵消], } # 在统计时将一个主关键词的所有同义词出现次数相加处理否定语境统计“出现次数”有时会误导比如“我们不采用区块链技术”。简单的词频统计无法区分。这需要更复杂的NLP情感分析或上下文判断超出了本基础项目的范围但在做结论时需要保持警惕。使用更高效的计数方法如果文档量巨大上万份关键词也很多纯Python循环可能较慢。可以考虑使用multiprocessing库进行多进程并行处理充分利用多核CPU。对于超大规模文本可以考虑将文本向量化后使用更高效的搜索算法但这属于高级优化范畴。4.3 常见错误与解决方案实录在运行上述代码时你很可能遇到以下问题问题现象可能原因解决方案ModuleNotFoundError: No module named fitz未正确安装PyMuPDFpip install PyMuPDF。注意导入时是import fitz但包名是PyMuPDF。pdfplumber提取表格为空或错乱PDF表格结构复杂或本质上是图片1. 尝试调整pdfplumber的extract_tables()参数如vertical_strategy,horizontal_strategy。2. 确认该页面是否为图片是则需OCR。关键词计数远低于/高于预期1. 文本提取不完整如漏了页眉。2. 匹配逻辑问题大小写、单词边界。3. 文档是扫描件。1. 打印几页提取的文本肉眼核对。2. 检查enhanced_keyword_count函数调试单个关键词的匹配。3. 用PDF阅读器检查文档属性看是否是“仅图像”。程序处理大量PDF时内存不足一次性将所有PDF内容加载到内存采用流式处理处理完一个PDF立即保存结果并释放内存再处理下一个。避免在pdf_data_dict中堆积所有原始文本。中文关键词匹配不到文本提取时编码问题或PDF字体特殊1. 确保所有文件操作读关键词、写结果使用utf-8编码。2. 尝试用pdfplumber并指定laparams参数改善中文布局分析page.extract_text(laparams{line_overlap: 0.7})pytesseract找不到Tesseract未安装Tesseract-OCR或路径不对1. 从GitHub安装Tesseract。2. 在代码中指定路径pytesseract.pytesseract.tesseract_cmd rC:\Program Files\Tesseract-OCR\tesseract.exe4.4 性能优化与扩展思路当项目规模扩大时以下几点可以帮助你增量处理如果每天都有新PDF加入可以设计一个机制只处理新的或修改过的文件。记录已处理文件的MD5哈希值或最后修改时间。结果数据库存储当结果数据量很大时将结果存入SQLite或MySQL数据库比CSV文件更便于查询和历史对比分析。添加日志系统使用Python的logging模块替代print可以更规范地记录信息、警告和错误方便后期排查。构建Web界面或自动化脚本使用Flask或Streamlit快速搭建一个上传PDF、输入关键词、查看结果的可视化界面交付给非技术同事使用。或者将主程序包装成定时任务如使用cron或APScheduler实现全自动化运行。这个项目虽然起点是一个简单的“关键词计数”但其内核是一个灵活的文档信息提取框架。掌握了它你就能应对许多类似的自动化文本处理需求从海量文档中解放双手让数据自己说话。