You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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')

关键改动说明

  1. 获取单元格坐标:通过page.cells获取每个表格单元格的bbox(边界框),记录单元格的全局行号、列号和坐标范围。
  2. 图片坐标转换:PyMuPDF的y轴方向与pdfplumber相反,需要将图片坐标转换为pdfplumber的体系,才能和单元格坐标匹配。
  3. 单元格匹配逻辑:判断图片的边界框是否落在单元格范围内(添加少量偏差容错),找到对应的单元格位置。
  4. 图片插入Excel:将图片调整大小适配单元格,插入到对应坐标的单元格中,而不是单独创建sheet。

注意事项

  • 如果PDF中有合并单元格,确保开启extract_merged_cells=True来正确识别单元格范围。
  • 图片大小调整系数可根据实际PDF的表格样式微调,保证显示效果。
  • 处理大文件时,可优化图片存储逻辑(比如不保存到本地,直接用字节流操作),减少IO开销。

内容的提问来源于stack exchange,提问作者dheeraj gakkampudi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 01:52:12