技术拾贝 | 一键提取PPT全文档文字与形状属性(颜色/尺寸/坐标)

发布于:2025-04-05 ⋅ 阅读:(12) ⋅ 点赞:(0)

本文提供三大核心功能模块,用于从PPT文件中提取结构化数据:

  1. 文本内容提取

    • 支持提取幻灯片中的所有文本内容(包括普通文本框、表格内文字以及分组形状中的文本)

    • 返回按幻灯片页码组织的文本集合

  2. 表格数据提取

    • 自动识别PPT中的表格元素

    • 以行列结构返回表格数据,保留原始表格组织形式

    • 支持多表格幻灯片处理

  3. 形状位置信息获取

    • 精确定位包含特定文本的形状坐标

    • 返回厘米为单位的精确位置和尺寸信息

当前版本注意事项

  • 功能限制:对于组合形状(Group Shapes)中的文本和属性,目前暂不支持提取

  • 格式要求:仅支持.pptx格式(Office 2007及以上版本)

  • 单位标准:位置信息统一以厘米(cm)为单位返回

完整代码如下

# -*- coding: utf-8 -*-
"""
PPT内容提取工具
功能:提取PPT中的文本内容、表格数据及形状位置信息
依赖库:python-pptx (需安装:pip install python-pptx)
注意:仅支持.pptx格式,不支持.ppt旧格式
"""

from pptx import Presentation  # 主操作库,版本建议>=0.6.21
from pptx.enum.shapes import MSO_SHAPE_TYPE  # 形状类型枚举常量
from pptx.util import Inches  # 单位转换工具(未使用但建议保留)


class PptAttribution:
    """PPT内容解析核心类,提供三大核心功能:
    1. 文本定位提取
    2. 表格数据提取
    3. 全文本内容提取
    """

    def ppt_slide_number(self, ppt_script):
        """
        获取PPT总页数(基础方法)

        参数:
            ppt_script (str): PPT文件路径

        返回:
            int: 幻灯片总页数

        注意:
            - 索引从0开始,如10页PPT对应0-9索引
            - 会实际加载文件,频繁调用建议缓存结果
        """
        prs = Presentation(ppt_script)  # 创建Presentation对象
        return len(prs.slides)  # 获取slides集合长度

    def get_high_weight_top_left_position(self, pptx_path, slide_index, str_assign='必要性说明'):
        """
        精确定位包含特定文本的形状坐标(单位:厘米)

        参数:
            pptx_path (str): 文件路径
            slide_index (int): 幻灯片索引(0-based)
            str_assign (str): 要定位的关键文本

        返回:
            tuple: (top, left, width, height) 四元组,单位厘米

        技术细节:
            1. 使用360000 EMU/cm的单位换算系数
            2. 只返回第一个匹配到的形状位置
            3. 未找到时返回全0坐标

        示例:
            >>> pos = get_high_weight_top_left_position('test.pptx', 0, '摘要')
            >>> print(f"位置:{pos[0]}cm, {pos[1]}cm")
        """
        prs = Presentation(pptx_path)
        try:
            slide = prs.slides[slide_index]  # 可能引发IndexError
        except IndexError:
            raise ValueError(f"无效幻灯片索引 {slide_index},最大应为 {len(prs.slides) - 1}")

        # 初始化返回值(单位:厘米)
        position = (0.0, 0.0, 0.0, 0.0)

        for shape in slide.shapes:
            if not shape.has_text_frame:
                continue

            # 提取形状内所有段落文本
            text = "\n".join(p.text for p in shape.text_frame.paragraphs)

            if str_assign in text:  # 使用in替代find提高可读性
                # EMU(English Metric Units)转换为厘米
                position = (
                    shape.top / 360000,  # 上边距
                    shape.left / 360000,  # 左边距
                    shape.width / 360000,  # 宽度
                    shape.height / 360000  # 高度
                )
                break  # 找到第一个匹配即退出

        return position

    def get_only_table_text_infor_from_ppt(self, template_ppt):
        """
        结构化提取PPT中所有表格数据

        参数:
            template_ppt (str): PPT文件路径

        返回:
            dict: 双层嵌套数据结构
                {
                    页索引: [
                        {'行数': 行号, '列数': 列号, '信息': 单元格内容},
                        ...
                    ],
                    ...
                }

        特殊处理:
            - 自动跳过空表格
            - 保留原始行列结构
            - 文本自动去除首尾空格
        """
        prs = Presentation(template_ppt)
        table_info_dic = {}

        for page_num, slide in enumerate(prs.slides):  # 使用enumerate更Pythonic
            page_tables = []

            for shape in slide.shapes:
                if not shape.has_table:
                    continue

                table = shape.table
                current_table = []

                # 遍历行列(注意:合并单元格会被识别为独立单元格)
                for row_idx, row in enumerate(table.rows):
                    for col_idx, cell in enumerate(row.cells):
                        text = cell.text_frame.text.strip()  # 自动去除空白字符
                        current_table.append({
                            '行数': row_idx,
                            '列数': col_idx,
                            '信息': text
                        })

                if current_table:  # 非空表格才保存
                    page_tables.extend(current_table)

            if page_tables:  # 当前页有表格才存入字典
                table_info_dic[page_num] = page_tables

        return table_info_dic

    def get_text_infor_from_ppt(self, template_ppt):
        """
        全文本提取(含表格/文本框/分组形状)

        参数:
            template_ppt (str): PPT文件路径

        返回:
            dict: {页码: [文本段落1, 文本段落2,...]}

        特性:
            1. 三重提取机制:
               - 表格内容(按行合并)
               - 独立文本框
               - 分组内的文本框
            2. 自动UTF-8编码处理
            3. 自动去除文本首尾空白
            4. 错误处理机制保障稳定性
        """
        prs = Presentation(template_ppt)
        txt_dic = {}

        for page_num, slide in enumerate(prs.slides):
            page_texts = []

            # 第一部分:提取表格文本(行合并模式)
            for shape in slide.shapes:
                try:
                    if hasattr(shape, "table"):
                        for row in shape.table.rows:
                            # 用"-"连接同行单元格,最后去除末尾分隔符
                            row_text = "-".join(
                                cell.text_frame.text.strip()
                                for cell in row.cells
                            )
                            if row_text:  # 非空行才添加
                                page_texts.append(row_text)
                except Exception as e:
                    print(f"页面{page_num}表格处理异常:{str(e)}")
                    continue

            # 第二部分:提取独立文本框
            for shape in slide.shapes:
                try:
                    if shape.has_text_frame:
                        text = shape.text.strip()
                        if text:
                            page_texts.append(text)
                except Exception as e:
                    print(f"页面{page_num}文本框处理异常:{str(e)}")
                    continue

            # 第三部分:提取分组形状内的文本
            for shape in slide.shapes:
                if shape.shape_type != MSO_SHAPE_TYPE.GROUP:
                    continue

                try:
                    for sub_shape in shape.shapes:
                        if sub_shape.has_text_frame:
                            text = sub_shape.text.strip()
                            if text:
                                page_texts.append(text)
                except Exception as e:
                    print(f"页面{page_num}分组文本处理异常:{str(e)}")
                    continue

            # 存入字典(即使为空列表也保留记录)
            txt_dic[page_num] = page_texts

        return txt_dic


# ====================== 使用示例 ======================
if __name__ == "__main__":
    analyzer = PptAttribution()  # 实例化分析器
    pptx_path = r'耗材红蓝榜.pptx'

    # 示例1:获取全文本
    text_data = analyzer.get_text_infor_from_ppt(pptx_path)
    print("=== 全文内容 ===")
    for page, contents in text_data.items():
        print(f"\n第{page + 1}页内容:")
        print("\n".join(f" - {text}" for text in contents))

    # 示例2:获取表格数据
    table_data = analyzer.get_only_table_text_infor_from_ppt(pptx_path)
    print("\n=== 表格数据 ===")
    for page, cells in table_data.items():
        print(f"\n第{page + 1}页表格:")
        for cell in cells:
            print(f"  行{cell['行数'] + 1}列{cell['列数'] + 1}: {cell['信息']}")

    # 示例3:定位特定元素
    print("\n=== 元素定位 ===")
    for i in range(analyzer.ppt_slide_number(pptx_path)):
        pos = analyzer.get_high_weight_top_left_position(
            pptx_path,
            slide_index=i,
            str_assign='红榜示范作用'
        )
        print(f"第{i + 1}页定位结果:{pos}")