Python一站式進(jìn)行提取文檔內(nèi)容(Word、Excel、PDF 和PPT)
代碼介紹
本文旨在系統(tǒng)介紹如何利用Python主流庫從四種常見格式文檔中提取原始文本

相關(guān)軟件安裝
第三方庫安裝
在cmd界面輸入:
pip install python-docx openpyxl pdfminer.six python-pptx
程序打包
完整代碼
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) # 綁定父對象,避免線程殘留
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"開始批量處理文件夾: {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} 個支持格式的文檔")
# 逐個處理文件
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} 個文件,成功 {self.success_count} 個,失敗 {self.fail_count} 個"
self.batch_finished.emit(summary, self.success_count, self.fail_count)
else:
# 處理單個文件
self.log_message.emit(f"開始處理單個文件: {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} 個,失敗: {self.fail_count} 個"
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"處理過程總錯誤: {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):
"""處理單個文件的提取和保存"""
file_name = os.path.basename(file_path)
file_ext = os.path.splitext(file_path)[1].lower()
# 確定單個文件的輸出路徑
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"跳過不支持的文件: {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"?? 無內(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 # 線程對象初始化
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("開始提取")
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ù)覽(僅顯示最后一個文件的前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)行提取操作,無法切換模式")
# 恢復(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)行提取操作,無法修改路徑")
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}")
# 自動建議輸出文件夾(原文件夾名+_提取結(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}")
# 自動建議輸出文件
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)行提取操作,無法修改路徑")
return
if not self.input_path:
QMessageBox.warning(self, "警告", f"請先選擇輸入{'文件夾' 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è)置且無運(yùn)行線程時,才能啟用開始按鈕
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):
"""開始提?。▎挝募?批量)"""
# 再次檢查路徑有效性
if not os.path.exists(self.input_path):
QMessageBox.warning(self, "錯誤", 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)建并啟動提取線程(綁定父對象,避免內(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)前提取操作嗎?已處理的文件會保留,未處理的將停止。",
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)容過長,僅顯示前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}")
# 清理線程對象(避免殘留)
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)擊開始提取

以上就是Python一站式進(jìn)行提取文檔內(nèi)容(Word、Excel、PDF 和PPT)的詳細(xì)內(nèi)容,更多關(guān)于Python提取文檔內(nèi)容的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
django-crontab 定時執(zhí)行任務(wù)方法的實(shí)現(xiàn)
這篇文章主要介紹了django-crontab 定時執(zhí)行任務(wù)方法的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Python獲取中國節(jié)假日數(shù)據(jù)記錄入JSON文件
項目系統(tǒng)內(nèi)置的日歷應(yīng)用為了提升用戶體驗(yàn),特別設(shè)置了在調(diào)休日期顯示“休”的UI圖標(biāo)功能,那么問題是這些調(diào)休數(shù)據(jù)從哪里來呢?我嘗試一種更為智能的方法:Python獲取中國節(jié)假日數(shù)據(jù)記錄入JSON文件2025-04-04
Python數(shù)據(jù)挖掘中常用的五種AutoEDA 工具總結(jié)
大家好,我們都知道在數(shù)據(jù)挖掘的過程中,數(shù)據(jù)探索性分析一直是非常耗時的一個環(huán)節(jié),但也是繞不開的一個環(huán)節(jié),本篇文章帶你盤點(diǎn)數(shù)據(jù)挖掘中常見的5種 AutoEDA 工具2021-11-11
Python使用pywifi模塊實(shí)現(xiàn)輕松查看WIFI密碼
這篇文章主要為大家詳細(xì)介紹了Python如何使用pywifi模塊實(shí)現(xiàn)輕松查看WIFI密碼,如果你不幸忘記了某個wifi的密碼,但是你大概知道密碼的一些構(gòu)成,也可以參考下面的腳本2025-05-05
Python實(shí)現(xiàn)疫情通定時自動填寫功能(附代碼)
這篇文章主要介紹了Python實(shí)現(xiàn)疫情通定時自動填寫功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05

