如何从PDF提取嵌入的CSV/Excel文件?已试PyPDF2等工具未果
从PDF提取嵌入CSV/Excel文件的解决方案
你之前的代码存在几个容易导致失败的问题,比如依赖旧版PyPDF2 API、遍历嵌入文件的方式不可靠,还有没覆盖PDF中嵌入文件的所有可能存储位置。下面是修复后的方案,还有更简便的替代工具:
问题排查
你的原代码问题点:
- 用了PyPDF2的旧版API(
PdfFileReader现在已经被PdfReader替代) - 用
index(f)找文件对象的方式有bug,如果PDF里有重名嵌入文件,会匹配到错误的对象 - 只处理了根目录下的
/EmbeddedFiles,但有些PDF会把嵌入文件存在表单域(/AcroForm)里 - 没有异常处理,一旦PDF结构不符合预期直接报错
修复后的PyPDF2代码
用最新API,覆盖更多嵌入文件存储场景,避免重名问题:
import PyPDF2 import os def extract_embedded_files(pdf_path, output_dir): # 自动创建输出文件夹 os.makedirs(output_dir, exist_ok=True) with open(pdf_path, 'rb') as pdf_file: reader = PyPDF2.PdfReader(pdf_file) attachments = {} # 处理文档根目录下的嵌入文件 root = reader.trailer['/Root'] if '/Names' in root and '/EmbeddedFiles' in root['/Names']: embedded_entries = root['/Names']['/EmbeddedFiles']['/Names'] # 成对遍历:文件名和文件对象是交替出现的,直接按步长2遍历更可靠 for i in range(0, len(embedded_entries), 2): filename = embedded_entries[i] if isinstance(filename, str): file_obj = embedded_entries[i+1].get_object() if '/EF' in file_obj and '/F' in file_obj['/EF']: file_data = file_obj['/EF']['/F'].get_data() attachments[filename] = file_data # 处理表单域中的嵌入文件(部分PDF会存在这里) if '/AcroForm' in root and '/Fields' in root['/AcroForm']: for field in root['/AcroForm']['/Fields']: field_obj = field.get_object() # 判断是否是文件域 if field_obj.get('/FT') == '/F' and '/EF' in field_obj and '/F' in field_obj['/EF']: filename = field_obj.get('/F', 'unknown_file') file_data = field_obj['/EF']['/F'].get_data() attachments[filename] = file_data # 保存所有提取到的文件 for filename, data in attachments.items(): # 清理文件名里的非法字符 safe_name = os.path.basename(filename).replace('/', '_').replace('\\', '_') save_path = os.path.join(output_dir, safe_name) with open(save_path, 'wb') as out_file: out_file.write(data) print(f"提取完成:{save_path}") # 替换成你的PDF路径和输出文件夹名 extract_embedded_files("your_pdf.pdf", "extracted_files")
更省心的替代方案:用PyMuPDF(fitz)
PyMuPDF对各种PDF结构的兼容性更好,代码也更简洁,推荐试试:
import fitz import os def extract_with_pymupdf(pdf_path, output_dir): os.makedirs(output_dir, exist_ok=True) doc = fitz.open(pdf_path) # 提取页面上的嵌入文件注释 for page in doc: for annot in page.annots(): if annot.type[0] == 17: # 标注类型为嵌入文件 filename = annot.info['content'] file_data = annot.get_file() safe_name = os.path.basename(filename).replace('/', '_').replace('\\', '_') save_path = os.path.join(output_dir, safe_name) with open(save_path, 'wb') as f: f.write(file_data) print(f"提取完成:{save_path}") # 提取文档级的嵌入文件 for name in doc.embfile_names(): file_data = doc.embfile_get(name) safe_name = os.path.basename(name).replace('/', '_').replace('\\', '_') save_path = os.path.join(output_dir, safe_name) with open(save_path, 'wb') as f: f.write(file_data) print(f"提取完成:{save_path}") # 使用示例 extract_with_pymupdf("your_pdf.pdf", "extracted_files")
额外提示
- 先安装需要的库:
pip install PyPDF2 pymupdf - 如果PDF加密了,要先解密:PyPDF2用
reader.decrypt("密码"),PyMuPDF在打开时加fitz.open(pdf_path, password="你的密码") - 如果还是提取不到,先试试用Adobe Acrobat手动提取,确认PDF里确实有可提取的嵌入文件(有些PDF的“嵌入”其实是把内容转成了PDF内容,不是真正的附件)
内容的提问来源于stack exchange,提问作者Manz
相关产品推荐
相关产品推荐

