最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python一站式進(jìn)行提取文檔內(nèi)容(Word、Excel、PDF 和PPT)

 更新時(shí)間:2025年09月23日 09:39:55   作者:阿幸軟件雜貨間  
這篇文章主要為大家詳細(xì)介紹了Python如何實(shí)現(xiàn)一站式提取文檔內(nèi)容,例如Word、Excel、PDF 和PPT,文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下

代碼介紹

本文旨在系統(tǒng)介紹如何利用Python主流庫(kù)從四種常見(jiàn)格式文檔中提取原始文本

相關(guān)軟件安裝

第三方庫(kù)安裝

在cmd界面輸入:

pip install python-docx openpyxl pdfminer.six python-pptx

程序打包

可以用這個(gè)一鍵打包文檔代碼

完整代碼

import os
import sys
from docx import Document
import openpyxl
from pdfminer.high_level import extract_text
from pptx import Presentation
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QLabel, 
                            QFileDialog, QTextEdit, QVBoxLayout, QHBoxLayout, 
                            QWidget, QProgressBar, QMessageBox, QGroupBox,
                            QRadioButton, QButtonGroup)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QObject
from PyQt5.QtGui import QFont


class BatchExtractorThread(QThread):
    """批量提取線程(支持單文件/文件夾)"""
    progress_updated = pyqtSignal(int)
    batch_finished = pyqtSignal(str, int, int)  # 匯總信息, 成功數(shù), 失敗數(shù)
    log_message = pyqtSignal(str)

    def __init__(self, input_path, output_path, is_folder=False, parent=None):
        super().__init__(parent)  # 綁定父對(duì)象,避免線程殘留
        self.input_path = input_path  # 輸入路徑(文件/文件夾)
        self.output_path = output_path  # 輸出路徑(文件/文件夾)
        self.is_folder = is_folder  # 是否為文件夾批量處理
        self.supported_exts = ['.docx', '.xlsx', '.pdf', '.pptx']  # 支持的格式
        self.success_count = 0  # 成功提取數(shù)
        self.fail_count = 0  # 提取失敗數(shù)
        self.is_running = False  # 線程運(yùn)行狀態(tài)標(biāo)記

    def run(self):
        self.is_running = True
        try:
            self.progress_updated.emit(5)
            if self.is_folder:
                # 處理文件夾:遍歷所有支持的文件
                self.log_message.emit(f"開(kāi)始批量處理文件夾: {self.input_path}")
                file_list = self.get_supported_files(self.input_path)
                total_files = len(file_list)
                
                if total_files == 0:
                    self.log_message.emit("文件夾中未找到支持格式的文檔")
                    self.batch_finished.emit("未找到可處理的文檔", 0, 0)
                    self.progress_updated.emit(100)
                    return
                
                self.log_message.emit(f"共發(fā)現(xiàn) {total_files} 個(gè)支持格式的文檔")
                
                # 逐個(gè)處理文件
                for idx, file_path in enumerate(file_list):
                    if not self.is_running:  # 檢查是否需要終止
                        self.log_message.emit("提取任務(wù)已被終止")
                        return
                    self.process_single_file(file_path, self.output_path)
                    # 更新整體進(jìn)度
                    progress = 5 + int((idx + 1) / total_files * 90)
                    self.progress_updated.emit(min(100, progress))
                    
                summary = f"批量處理完成!共處理 {total_files} 個(gè)文件,成功 {self.success_count} 個(gè),失敗 {self.fail_count} 個(gè)"
                self.batch_finished.emit(summary, self.success_count, self.fail_count)
            else:
                # 處理單個(gè)文件
                self.log_message.emit(f"開(kāi)始處理單個(gè)文件: {self.input_path}")
                if self.is_running:  # 確保線程未被終止
                    self.process_single_file(self.input_path, self.output_path, is_single=True)
                summary = f"單文件處理完成!成功: {self.success_count} 個(gè),失敗: {self.fail_count} 個(gè)"
                self.batch_finished.emit(summary, self.success_count, self.fail_count)
                self.progress_updated.emit(100)
                
        except Exception as e:
            self.log_message.emit(f"處理過(guò)程總錯(cuò)誤: {str(e)}")
            self.batch_finished.emit(f"處理失敗: {str(e)}", 0, 1)
        finally:
            self.is_running = False  # 標(biāo)記線程結(jié)束

    def get_supported_files(self, folder_path):
        """獲取文件夾內(nèi)所有支持格式的文件(遞歸遍歷子文件夾)"""
        supported_files = []
        for root, _, files in os.walk(folder_path):
            for file in files:
                file_ext = os.path.splitext(file)[1].lower()
                if file_ext in self.supported_exts:
                    supported_files.append(os.path.join(root, file))
        return supported_files

    def process_single_file(self, file_path, output_root, is_single=False):
        """處理單個(gè)文件的提取和保存"""
        file_name = os.path.basename(file_path)
        file_ext = os.path.splitext(file_path)[1].lower()
        
        # 確定單個(gè)文件的輸出路徑
        if is_single:
            # 單文件模式:直接使用用戶指定的輸出路徑
            save_path = output_root
        else:
            # 批量模式:保持原文件夾結(jié)構(gòu),輸出到指定根目錄
            relative_path = os.path.relpath(os.path.dirname(file_path), self.input_path)
            output_dir = os.path.join(output_root, relative_path)
            os.makedirs(output_dir, exist_ok=True)
            # 生成輸出文件名(原文件名+_提取結(jié)果.txt)
            base_name = os.path.splitext(file_name)[0]
            save_path = os.path.join(output_dir, f"{base_name}_提取結(jié)果.txt")

        try:
            self.log_message.emit(f"正在處理: {file_name}")
            # 根據(jù)文件類型提取內(nèi)容
            if file_ext == '.docx':
                content = self.doc_extract(file_path)
                file_type = "Word"
            elif file_ext == '.xlsx':
                content = self.excel_extract(file_path)
                file_type = "Excel"
            elif file_ext == '.pdf':
                content = self.pdf_extract(file_path)
                file_type = "PDF"
            elif file_ext == '.pptx':
                content = self.ppt_extract(file_path)
                file_type = "PowerPoint"
            else:
                self.log_message.emit(f"跳過(guò)不支持的文件: {file_name}")
                self.fail_count += 1
                return

            # 保存內(nèi)容
            if content:
                self.save_content(content, save_path)
                self.log_message.emit(f"? 成功: {file_name} -> 保存至 {os.path.basename(save_path)}")
                self.success_count += 1
            else:
                self.log_message.emit(f"??  無(wú)內(nèi)容: {file_name}(未生成輸出文件)")
                self.fail_count += 1

        except Exception as e:
            self.log_message.emit(f"? 失敗: {file_name} - {str(e)}")
            self.fail_count += 1

    def doc_extract(self, file_path) -> str:
        """提取 Word (.docx) 文檔內(nèi)容"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        doc = Document(file_path)
        content = [para.text for para in doc.paragraphs]
        return '\n'.join(content)

    def excel_extract(self, file_path) -> str:
        """提取 Excel (.xlsx) 文檔內(nèi)容"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        data = []
        wb = openpyxl.load_workbook(file_path)
        for sheet in wb.sheetnames:
            data.append(f"=== 工作表: {sheet} ===")
            ws = wb[sheet]
            for i in range(1, ws.max_row + 1):
                row_data = []
                for j in range(1, ws.max_column + 1):
                    cell_val = str(ws.cell(i, j).value) if ws.cell(i, j).value is not None else ""
                    row_data.append(cell_val)
                data.append("\t".join(row_data))
        return '\n'.join(data)

    def pdf_extract(self, file_path) -> str:
        """提取 PDF (.pdf) 文檔內(nèi)容"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        return extract_text(file_path)

    def ppt_extract(self, file_path) -> str:
        """提取 PowerPoint (.pptx) 文檔內(nèi)容"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        content = []
        ppt = Presentation(file_path)
        for slide_idx, slide in enumerate(ppt.slides, 1):
            content.append(f"=== 幻燈片 {slide_idx} ===")
            for shape in slide.shapes:
                if hasattr(shape, 'text') and shape.text:
                    content.append(shape.text)
        return '\n'.join(content)

    def save_content(self, content, save_path) -> None:
        """保存提取的內(nèi)容到指定路徑"""
        with open(save_path, 'w', encoding='utf-8') as f:
            f.write(content)

    def stop(self):
        """終止線程(安全退出)"""
        self.is_running = False


class DocumentExtractorApp(QMainWindow):
    """文檔提取器主窗口(支持單文件/文件夾批量處理)"""
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.extractor_thread = None  # 線程對(duì)象初始化
        
    def init_ui(self):
        """初始化用戶界面(優(yōu)化布局間距)"""
        self.setWindowTitle("一站式提取Word、Excel、PDF 和PPT文檔內(nèi)容@阿幸")
        self.setGeometry(100, 100, 1000, 750)
        
        # 設(shè)置中文字體(優(yōu)化字體大小和間距)
        base_font = QFont()
        base_font.setFamily("SimHei")
        base_font.setPointSize(10)  # 基礎(chǔ)字體大小
        self.setFont(base_font)
        
        # 主布局(增加整體內(nèi)邊距)
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(15, 15, 15, 15)  # 主布局四周內(nèi)邊距
        main_layout.setSpacing(12)  # 布局內(nèi)組件間距
        
        # 1. 處理模式選擇(單文件/文件夾)- 核心優(yōu)化區(qū)域
        mode_group = QGroupBox("處理模式")
        mode_group.setFont(QFont("SimHei", 11, QFont.Bold))  # 分組標(biāo)題加粗
        mode_layout = QHBoxLayout()
        # 增加模式布局內(nèi)邊距和間距,解決文字擁擠
        mode_layout.setContentsMargins(20, 15, 20, 15)  # 分組內(nèi)邊距
        mode_layout.setSpacing(30)  # 單選按鈕間距
        
        self.single_mode_radio = QRadioButton("單文件處理")
        self.folder_mode_radio = QRadioButton("文件夾批量處理")
        # 優(yōu)化單選按鈕字體和大小
        radio_font = QFont("SimHei", 10)
        self.single_mode_radio.setFont(radio_font)
        self.folder_mode_radio.setFont(radio_font)
        
        self.mode_btn_group = QButtonGroup(self)
        self.mode_btn_group.addButton(self.single_mode_radio, 0)
        self.mode_btn_group.addButton(self.folder_mode_radio, 1)
        self.single_mode_radio.setChecked(True)  # 默認(rèn)單文件模式
        
        # 模式切換事件
        self.mode_btn_group.buttonClicked.connect(self.switch_mode)
        
        mode_layout.addWidget(self.single_mode_radio)
        mode_layout.addWidget(self.folder_mode_radio)
        mode_group.setLayout(mode_layout)
        main_layout.addWidget(mode_group)
        
        # 2. 路徑選擇區(qū)域
        path_group = QGroupBox("路徑設(shè)置")
        path_group.setFont(QFont("SimHei", 11, QFont.Bold))
        path_layout = QVBoxLayout()
        path_layout.setContentsMargins(15, 15, 15, 15)
        path_layout.setSpacing(10)
        
        # 輸入路徑選擇(文件/文件夾)
        input_layout = QHBoxLayout()
        input_layout.setSpacing(10)  # 標(biāo)簽和按鈕間距
        self.input_label = QLabel("未選擇輸入(文件/文件夾)")
        self.input_label.setWordWrap(True)
        self.input_label.setStyleSheet("border: 1px solid #ccc; padding: 8px; min-height: 35px;")
        self.input_label.setFont(QFont("SimHei", 9))  # 標(biāo)簽字體稍小,避免擁擠
        
        self.select_input_btn = QPushButton("選擇輸入路徑")
        self.select_input_btn.setFont(QFont("SimHei", 10))
        self.select_input_btn.setMinimumWidth(120)  # 固定按鈕寬度,避免變形
        self.select_input_btn.clicked.connect(self.select_input_path)
        
        input_layout.addWidget(self.input_label, 7)
        input_layout.addWidget(self.select_input_btn, 3)
        
        # 輸出路徑選擇(文件/文件夾)
        output_layout = QHBoxLayout()
        output_layout.setSpacing(10)
        self.output_label = QLabel("未選擇輸出(文件/文件夾)")
        self.output_label.setWordWrap(True)
        self.output_label.setStyleSheet("border: 1px solid #ccc; padding: 8px; min-height: 35px;")
        self.output_label.setFont(QFont("SimHei", 9))
        
        self.select_output_btn = QPushButton("選擇輸出路徑")
        self.select_output_btn.setFont(QFont("SimHei", 10))
        self.select_output_btn.setMinimumWidth(120)
        self.select_output_btn.clicked.connect(self.select_output_path)
        
        output_layout.addWidget(self.output_label, 7)
        output_layout.addWidget(self.select_output_btn, 3)
        
        # 添加到路徑布局
        path_layout.addLayout(input_layout)
        path_layout.addLayout(output_layout)
        path_group.setLayout(path_layout)
        main_layout.addWidget(path_group)
        
        # 3. 操作區(qū)域(新增終止按鈕)
        action_layout = QHBoxLayout()
        action_layout.setSpacing(15)  # 按鈕間距
        
        self.start_btn = QPushButton("開(kāi)始提取")
        self.start_btn.setStyleSheet("font-size: 14px; padding: 10px; background-color: #4CAF50; color: white;")
        self.start_btn.setFont(QFont("SimHei", 10, QFont.Bold))
        self.start_btn.setMinimumWidth(150)
        self.start_btn.clicked.connect(self.start_extraction)
        self.start_btn.setEnabled(False)
        
        self.stop_btn = QPushButton("終止提取")
        self.stop_btn.setStyleSheet("font-size: 14px; padding: 10px; background-color: #f44336; color: white;")
        self.stop_btn.setFont(QFont("SimHei", 10, QFont.Bold))
        self.stop_btn.setMinimumWidth(150)
        self.stop_btn.clicked.connect(self.stop_extraction)
        self.stop_btn.setEnabled(False)  # 默認(rèn)禁用
        
        self.clear_btn = QPushButton("清空日志")
        self.clear_btn.setStyleSheet("font-size: 14px; padding: 10px;")
        self.clear_btn.setFont(QFont("SimHei", 10))
        self.clear_btn.setMinimumWidth(150)
        self.clear_btn.clicked.connect(self.clear_logs)
        
        action_layout.addWidget(self.start_btn)
        action_layout.addWidget(self.stop_btn)
        action_layout.addWidget(self.clear_btn)
        main_layout.addLayout(action_layout)
        
        # 4. 進(jìn)度條
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        self.progress_bar.setStyleSheet("margin-bottom: 10px; height: 25px;")
        main_layout.addWidget(self.progress_bar)
        
        # 5. 結(jié)果預(yù)覽區(qū)域
        result_group = QGroupBox("提取結(jié)果預(yù)覽(僅顯示最后一個(gè)文件的前2000字符)")
        result_group.setFont(QFont("SimHei", 11, QFont.Bold))
        result_layout = QVBoxLayout()
        result_layout.setContentsMargins(10, 10, 10, 10)
        
        self.result_text = QTextEdit()
        self.result_text.setReadOnly(True)
        self.result_text.setFont(QFont("SimHei", 9))
        result_layout.addWidget(self.result_text)
        
        result_group.setLayout(result_layout)
        main_layout.addWidget(result_group, 2)
        
        # 6. 日志區(qū)域
        log_group = QGroupBox("操作日志")
        log_group.setFont(QFont("SimHei", 11, QFont.Bold))
        log_layout = QVBoxLayout()
        log_layout.setContentsMargins(10, 10, 10, 10)
        
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        self.log_text.setMaximumHeight(120)
        self.log_text.setFont(QFont("SimHei", 9))
        log_layout.addWidget(self.log_text)
        
        log_group.setLayout(log_layout)
        main_layout.addWidget(log_group)
        
        # 設(shè)置中心部件
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)
        
        # 初始化變量
        self.input_path = ""  # 輸入路徑(文件/文件夾)
        self.output_path = ""  # 輸出路徑(文件/文件夾)
        self.is_folder_mode = False  # 當(dāng)前是否為文件夾模式

    def switch_mode(self):
        """切換處理模式(單文件/文件夾)"""
        # 切換模式前先檢查是否有正在運(yùn)行的線程
        if self.extractor_thread and self.extractor_thread.isRunning():
            QMessageBox.warning(self, "警告", "正在進(jìn)行提取操作,無(wú)法切換模式")
            # 恢復(fù)原選擇狀態(tài)
            if self.is_folder_mode:
                self.folder_mode_radio.setChecked(True)
            else:
                self.single_mode_radio.setChecked(True)
            return
        
        self.is_folder_mode = (self.mode_btn_group.checkedId() == 1)
        # 清空現(xiàn)有路徑并更新提示
        self.input_path = ""
        self.output_path = ""
        self.input_label.setText("未選擇輸入文件夾" if self.is_folder_mode else "未選擇輸入文件")
        self.output_label.setText("未選擇輸出文件夾" if self.is_folder_mode else "未選擇輸出文件")
        self.start_btn.setEnabled(False)
        self.log_message(f"已切換至{'文件夾批量處理' if self.is_folder_mode else '單文件處理'}模式")

    def select_input_path(self):
        """選擇輸入路徑(根據(jù)模式選擇文件或文件夾)"""
        # 選擇路徑前檢查線程狀態(tài)
        if self.extractor_thread and self.extractor_thread.isRunning():
            QMessageBox.warning(self, "警告", "正在進(jìn)行提取操作,無(wú)法修改路徑")
            return
            
        if self.is_folder_mode:
            # 文件夾模式:選擇文件夾
            folder_path = QFileDialog.getExistingDirectory(
                self, "選擇輸入文件夾", "", QFileDialog.ShowDirsOnly
            )
            if folder_path:
                self.input_path = folder_path
                self.input_label.setText(f"輸入文件夾: {folder_path}")
                self.log_message(f"已選擇輸入文件夾: {folder_path}")
                # 自動(dòng)建議輸出文件夾(原文件夾名+_提取結(jié)果)
                if not self.output_path:
                    folder_name = os.path.basename(folder_path)
                    self.output_path = os.path.join(os.path.dirname(folder_path), f"{folder_name}_提取結(jié)果")
                    self.output_label.setText(f"輸出文件夾: {self.output_path}")
        else:
            # 單文件模式:選擇文件
            file_path, _ = QFileDialog.getOpenFileName(
                self, "選擇文檔文件", "", 
                "支持的文件 (*.docx *.xlsx *.pdf *.pptx);;Word 文件 (*.docx);;Excel 文件 (*.xlsx);;PDF 文件 (*.pdf);;PPT 文件 (*.pptx);;所有文件 (*)"
            )
            if file_path:
                self.input_path = file_path
                self.input_label.setText(f"輸入文件: {os.path.basename(file_path)}")
                self.log_message(f"已選擇輸入文件: {file_path}")
                # 自動(dòng)建議輸出文件
                if not self.output_path:
                    base_name = os.path.splitext(os.path.basename(file_path))[0]
                    self.output_path = os.path.join(os.path.dirname(file_path), f"{base_name}_提取結(jié)果.txt")
                    self.output_label.setText(f"輸出文件: {os.path.basename(self.output_path)}")
        
        self.check_btn_state()

    def select_output_path(self):
        """選擇輸出路徑(根據(jù)模式選擇文件或文件夾)"""
        # 選擇路徑前檢查線程狀態(tài)
        if self.extractor_thread and self.extractor_thread.isRunning():
            QMessageBox.warning(self, "警告", "正在進(jìn)行提取操作,無(wú)法修改路徑")
            return
            
        if not self.input_path:
            QMessageBox.warning(self, "警告", f"請(qǐng)先選擇輸入{'文件夾' if self.is_folder_mode else '文件'}")
            return
        
        if self.is_folder_mode:
            # 文件夾模式:選擇輸出文件夾
            folder_path = QFileDialog.getExistingDirectory(
                self, "選擇輸出文件夾", os.path.dirname(self.input_path), QFileDialog.ShowDirsOnly
            )
            if folder_path:
                self.output_path = folder_path
                self.output_label.setText(f"輸出文件夾: {folder_path}")
                self.log_message(f"已選擇輸出文件夾: {folder_path}")
        else:
            # 單文件模式:選擇輸出文件
            default_name = os.path.splitext(os.path.basename(self.input_path))[0] + "_提取結(jié)果.txt"
            file_path, _ = QFileDialog.getSaveFileName(
                self, "保存提取結(jié)果", 
                os.path.join(os.path.dirname(self.input_path), default_name),
                "文本文件 (*.txt);;所有文件 (*)"
            )
            if file_path:
                self.output_path = file_path
                self.output_label.setText(f"輸出文件: {os.path.basename(file_path)}")
                self.log_message(f"已選擇輸出文件: {file_path}")
        
        self.check_btn_state()

    def check_btn_state(self):
        """檢查按鈕啟用狀態(tài)"""
        # 只有路徑都設(shè)置且無(wú)運(yùn)行線程時(shí),才能啟用開(kāi)始按鈕
        can_start = bool(self.input_path) and bool(self.output_path)
        if self.extractor_thread and self.extractor_thread.isRunning():
            can_start = False
        self.start_btn.setEnabled(can_start)

    def log_message(self, message):
        """添加日志信息"""
        self.log_text.append(message)
        self.log_text.moveCursor(self.log_text.textCursor().End)

    def clear_logs(self):
        """清空日志和預(yù)覽"""
        # 提取中也允許清空日志
        self.log_text.clear()
        self.result_text.clear()
        self.log_message("已清空日志和結(jié)果預(yù)覽")

    def start_extraction(self):
        """開(kāi)始提取(單文件/批量)"""
        # 再次檢查路徑有效性
        if not os.path.exists(self.input_path):
            QMessageBox.warning(self, "錯(cuò)誤", f"輸入{'文件夾' if self.is_folder_mode else '文件'}不存在")
            return
            
        # 禁用相關(guān)按鈕,啟用終止按鈕
        self.select_input_btn.setEnabled(False)
        self.select_output_btn.setEnabled(False)
        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        self.result_text.clear()
        
        # 創(chuàng)建并啟動(dòng)提取線程(綁定父對(duì)象,避免內(nèi)存泄漏)
        self.extractor_thread = BatchExtractorThread(
            input_path=self.input_path,
            output_path=self.output_path,
            is_folder=self.is_folder_mode,
            parent=self  # 關(guān)鍵:綁定到主窗口,確保線程隨窗口生命周期管理
        )
        self.extractor_thread.progress_updated.connect(self.update_progress)
        self.extractor_thread.batch_finished.connect(self.on_extraction_finished)
        self.extractor_thread.log_message.connect(self.log_message)
        
        # 單文件模式下綁定預(yù)覽功能
        if not self.is_folder_mode:
            self.extractor_thread.process_single_file = self.wrap_single_file_process(
                self.extractor_thread.process_single_file
            )
        
        self.extractor_thread.start()

    def stop_extraction(self):
        """終止提取操作"""
        if self.extractor_thread and self.extractor_thread.isRunning():
            reply = QMessageBox.question(
                self, "確認(rèn)終止", 
                "確定要終止當(dāng)前提取操作嗎?已處理的文件會(huì)保留,未處理的將停止。",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.extractor_thread.stop()  # 安全終止線程
                self.log_message("已終止提取操作")
                # 恢復(fù)按鈕狀態(tài)
                self.select_input_btn.setEnabled(True)
                self.select_output_btn.setEnabled(True)
                self.start_btn.setEnabled(True)
                self.stop_btn.setEnabled(False)
                self.progress_bar.setValue(0)
                self.progress_bar.setVisible(False)

    def wrap_single_file_process(self, original_func):
        """包裝單文件處理函數(shù),用于獲取預(yù)覽內(nèi)容"""
        def wrapper(file_path, output_root, is_single=False):
            # 先執(zhí)行原處理邏輯
            original_func(file_path, output_root, is_single)
            # 讀取保存的文件內(nèi)容用于預(yù)覽
            if os.path.exists(output_root):
                with open(output_root, 'r', encoding='utf-8') as f:
                    content = f.read()
                # 顯示前2000字符預(yù)覽
                preview = content[:2000]
                if len(content) > 2000:
                    preview += "\n\n... 內(nèi)容過(guò)長(zhǎng),僅顯示前2000字符 ..."
                self.result_text.setText(preview)
        return wrapper

    def update_progress(self, value):
        """更新進(jìn)度條"""
        self.progress_bar.setValue(value)

    def on_extraction_finished(self, summary, success_count, fail_count):
        """提取完成回調(diào)(核心修復(fù):保持軟件運(yùn)行)"""
        # 關(guān)鍵修復(fù):恢復(fù)所有操作按鈕狀態(tài),不終止程序
        self.select_input_btn.setEnabled(True)
        self.select_output_btn.setEnabled(True)
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)  # 提取完成后禁用終止按鈕
        self.progress_bar.setVisible(False)  # 隱藏進(jìn)度條
        
        # 顯示匯總信息(使用information而非critical,避免誤操作)
        QMessageBox.information(self, "處理完成", summary)
        self.log_message(f"\n{summary}")
        
        # 清理線程對(duì)象(避免殘留)
        self.extractor_thread = None

    def closeEvent(self, event):
        """窗口關(guān)閉事件(安全處理線程)"""
        if self.extractor_thread and self.extractor_thread.isRunning():
            reply = QMessageBox.question(
                self, "確認(rèn)關(guān)閉", 
                "正在進(jìn)行提取操作,強(qiáng)制關(guān)閉可能導(dǎo)致文件損壞,確定要關(guān)閉嗎?",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.extractor_thread.stop()
                self.extractor_thread.wait()  # 等待線程安全退出
                event.accept()
            else:
                event.ignore()
        else:
            event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 確保中文顯示正常
    app.setFont(QFont("SimHei", 10))
    window = DocumentExtractorApp()
    window.show()
    # 關(guān)鍵修復(fù):正確的事件循環(huán)退出邏輯
    exit_code = app.exec_()
    # 程序退出前確保線程已終止
    if hasattr(window, 'extractor_thread') and window.extractor_thread and window.extractor_thread.isRunning():
        window.extractor_thread.stop()
        window.extractor_thread.wait()
    sys.exit(exit_code)

軟件使用

可以選擇單文件也可以選擇文件夾

點(diǎn)擊開(kāi)始提取

以上就是Python一站式進(jìn)行提取文檔內(nèi)容(Word、Excel、PDF 和PPT)的詳細(xì)內(nèi)容,更多關(guān)于Python提取文檔內(nèi)容的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Python利用pywin32實(shí)現(xiàn)自動(dòng)操作電腦

    Python利用pywin32實(shí)現(xiàn)自動(dòng)操作電腦

    在windows系統(tǒng)上,重復(fù)性的操作可以用Python腳本來(lái)完成,其中常用的模塊是win32gui、win32con、win32api,要使用這三個(gè)模塊需要先安裝pywin32。本文就為大家介紹了如何利用這些模塊實(shí)現(xiàn)自動(dòng)操作電腦,感興趣的可以了解一下
    2022-11-11
  • python中單雙下劃線的區(qū)別對(duì)比分析

    python中單雙下劃線的區(qū)別對(duì)比分析

    Python中存在一些特殊的方法,有些方法以雙下劃線 “__” 開(kāi)頭和結(jié)尾,它們是Python的魔法函數(shù),比如__init__()和__str__等等,不用要這種方式命名自己的變量或者函數(shù),這篇文章主要介紹了python里面單雙下劃線的區(qū)別,需要的朋友可以參考下
    2023-04-04
  • python中for語(yǔ)句簡(jiǎn)單遍歷數(shù)據(jù)的方法

    python中for語(yǔ)句簡(jiǎn)單遍歷數(shù)據(jù)的方法

    這篇文章主要介紹了python中for語(yǔ)句簡(jiǎn)單遍歷數(shù)據(jù)的方法,以一個(gè)簡(jiǎn)單實(shí)例形式分析了Python中for語(yǔ)句遍歷數(shù)據(jù)的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-05-05
  • python?selenium參數(shù)詳解和實(shí)現(xiàn)案例

    python?selenium參數(shù)詳解和實(shí)現(xiàn)案例

    這篇文章主要介紹了python?selenium參數(shù)詳解和實(shí)現(xiàn)案例,無(wú)頭模式添加,可以讓selenium模擬登錄,進(jìn)入到后臺(tái)運(yùn)行,本文以登錄打開(kāi)公司內(nèi)網(wǎng)下載數(shù)據(jù)為例,給大家詳細(xì)講解,需要的朋友可以參考下
    2022-10-10
  • Numpy數(shù)組轉(zhuǎn)置的兩種實(shí)現(xiàn)方法

    Numpy數(shù)組轉(zhuǎn)置的兩種實(shí)現(xiàn)方法

    下面小編就為大家分享一篇Numpy數(shù)組轉(zhuǎn)置的兩種實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • 解決pyinstaller打包運(yùn)行程序時(shí)出現(xiàn)缺少plotly庫(kù)問(wèn)題

    解決pyinstaller打包運(yùn)行程序時(shí)出現(xiàn)缺少plotly庫(kù)問(wèn)題

    這篇文章主要介紹了解決pyinstaller打包運(yùn)行程序時(shí)出現(xiàn)缺少plotly庫(kù)問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 使用python快速生成nodejs項(xiàng)目文件結(jié)構(gòu)

    使用python快速生成nodejs項(xiàng)目文件結(jié)構(gòu)

    這篇文章主要介紹了如何使用Python快速生成nodejs項(xiàng)目文件結(jié)構(gòu),主要是為了解決AI Studio生成的Node.js程序的存放文件夾并快速生成問(wèn)題的工具軟件,有需要的可以了解下
    2026-01-01
  • Pytorch抽取網(wǎng)絡(luò)層的Feature Map(Vgg)實(shí)例

    Pytorch抽取網(wǎng)絡(luò)層的Feature Map(Vgg)實(shí)例

    今天小編就為大家分享一篇Pytorch抽取網(wǎng)絡(luò)層的Feature Map(Vgg)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-08-08
  • Django設(shè)置Postgresql的操作

    Django設(shè)置Postgresql的操作

    這篇文章主要介紹了Django設(shè)置Postgresql的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-05-05
  • 使用go和python遞歸刪除.ds store文件的方法

    使用go和python遞歸刪除.ds store文件的方法

    使用python和go遞歸刪除.DS_Store文件,.DS_Store (英文全稱 Desktop Services Store)是一種由蘋果公司的Mac OS X操作系統(tǒng)所創(chuàng)造的隱藏文件,目的在于存貯文件夾的自定義屬性
    2014-01-01

最新評(píng)論

都安| 通许县| 马边| 阳东县| 恭城| 保德县| 晋州市| 双桥区| 石屏县| 日喀则市| 额尔古纳市| 烟台市| 宾阳县| 曲阜市| 洪洞县| 丰都县| 景德镇市| 黄骅市| 长岭县| 启东市| 柘城县| 齐齐哈尔市| 芒康县| 南丰县| 潮安县| 福清市| 建始县| 麦盖提县| 来安县| 洛浦县| 茶陵县| 江城| 泸水县| 桑植县| 牡丹江市| 稷山县| 陆丰市| 隆昌县| 南京市| 屯昌县| 黎城县|