如何用pdfplumber及PyMuPDF提取PDF表格列图片并导入Excel
解决PDF表格列内图片提取并对应插入Excel的问题
你当前的代码能提取表格数据和图片,但无法将图片关联到表格的对应行。要实现图片按原位置插入Excel对应单元格,核心是通过坐标匹配图片所属的表格单元格,以下是修改后的完整实现:
from flask import Flask, request, send_file, jsonify import pdfplumber import pandas as pd import os from flask_cors import CORS import io import fitz # PyMuPDF from PIL import Image from openpyxl import Workbook from openpyxl.drawing.image import Image as ExcelImage from openpyxl.utils.dataframe import dataframe_to_rows from openpyxl.utils import get_column_letter app = Flask(__name__) CORS(app) @app.route('/') def home(): return jsonify({'message': 'Welcome to the PDF to Excel converter API!'}) @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return jsonify({'error': 'No file part'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'No selected file'}), 400 if file: # 确保上传目录存在 if not os.path.exists('uploads'): os.makedirs('uploads') # 保存上传的PDF pdf_path = os.path.join('uploads', file.filename) file.save(pdf_path) excel_path = pdf_path.replace('.pdf', '.xlsx') try: with pdfplumber.open(pdf_path) as pdf, fitz.open(pdf_path) as mupdf_pdf: wb = Workbook() ws_data = wb.active ws_data.title = "Data" data_rows = [] output_dir = 'images' os.makedirs(output_dir, exist_ok=True) for page_num, page in enumerate(pdf.pages, start=1): # 提取表格及单元格坐标 tables = page.extract_tables() table_cells = [] # 存储每个单元格的坐标和位置信息 (row_idx, col_idx, bbox) all_tables = [] for table_idx, table in enumerate(tables): df = pd.DataFrame(table) all_tables.append(df) # 获取表格的单元格bbox信息 for row_idx, row in enumerate(page.extract_table(table_settings={"extract_merged_cells": True})): for col_idx, cell in enumerate(row): cell_bbox = page.cells[table_idx][row_idx][col_idx]['bbox'] table_cells.append({ "row": row_idx + len(data_rows), # 全局行号(累加之前的行数) "col": col_idx, "bbox": cell_bbox }) if all_tables: combined_df = pd.concat(all_tables, ignore_index=True) if page_num == 1: data_rows.extend(dataframe_to_rows(combined_df, index=False, header=True)) else: data_rows.extend(dataframe_to_rows(combined_df, index=False, header=False)) # 提取当前页图片并匹配单元格 mupdf_page = mupdf_pdf[page_num - 1] image_list = mupdf_page.get_images(full=True) for img_index, img in enumerate(image_list): xref = img[0] base_image = mupdf_pdf.extract_image(xref) image_bytes = base_image["image"] image_ext = base_image["ext"] image = Image.open(io.BytesIO(image_bytes)) # 获取图片在PDF中的坐标(转换为pdfplumber的坐标体系) img_rect = mupdf_page.get_image_rects(img)[0] # PyMuPDF的y轴向上,pdfplumber向下,转换坐标 page_height = page.height img_bbox = (img_rect.x0, page_height - img_rect.y1, img_rect.x1, page_height - img_rect.y0) # 匹配图片所属的单元格 matched_cell = None for cell in table_cells: cell_bbox = cell["bbox"] # 判断图片是否在单元格范围内(允许少量偏差) if (img_bbox[0] >= cell_bbox[0] - 5 and img_bbox[1] >= cell_bbox[1] - 5 and img_bbox[2] <= cell_bbox[2] + 5 and img_bbox[3] <= cell_bbox[3] + 5): matched_cell = cell break if matched_cell: # 保存图片 image_filename = f'{output_dir}/page_{page_num}_img_{img_index + 1}.{image_ext}' image.save(image_filename) # 插入图片到对应单元格 img_excel = ExcelImage(image_filename) # 调整图片大小适配单元格(可选) cell_width = ws_data.column_dimensions[get_column_letter(matched_cell["col"] + 1)].width or 8.43 cell_height = ws_data.row_dimensions[matched_cell["row"] + 1].height or 15 img_excel.width = cell_width * 7.2 # 转换为像素 img_excel.height = cell_height * 1.33 # 单元格坐标:列字母+行号(注意Excel行号从1开始) cell_coord = f'{get_column_letter(matched_cell["col"] + 1)}{matched_cell["row"] + 1}' ws_data.add_image(img_excel, cell_coord) # 写入表格数据 for row in data_rows: ws_data.append(row) # 保存Excel wb.save(excel_path) return send_file(excel_path, as_attachment=True) except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': if not os.path.exists('uploads'): os.makedirs('uploads') app.run(debug=True, port=2000, host='0.0.0.0')
关键改动说明
- 获取单元格坐标:通过
page.cells获取每个表格单元格的bbox(边界框),记录单元格的全局行号、列号和坐标范围。 - 图片坐标转换:PyMuPDF的y轴方向与pdfplumber相反,需要将图片坐标转换为pdfplumber的体系,才能和单元格坐标匹配。
- 单元格匹配逻辑:判断图片的边界框是否落在单元格范围内(添加少量偏差容错),找到对应的单元格位置。
- 图片插入Excel:将图片调整大小适配单元格,插入到对应坐标的单元格中,而不是单独创建sheet。
注意事项
- 如果PDF中有合并单元格,确保开启
extract_merged_cells=True来正确识别单元格范围。 - 图片大小调整系数可根据实际PDF的表格样式微调,保证显示效果。
- 处理大文件时,可优化图片存储逻辑(比如不保存到本地,直接用字节流操作),减少IO开销。
内容的提问来源于stack exchange,提问作者dheeraj gakkampudi
相关产品推荐
相关产品推荐

