前言:日常生活中,我们有时后就会遇见这样的情形:我们需要将给定的数据进行可视化处理,同时保证呈现比较良好的量化效果。这时候我们可能就会用到词云图。词云图(Word cloud)又称文字云,是一种文本数据的图片视觉表达方式,一般是由词汇组成类似云的图形,用于展示大量文本数据。词云这个概念首先是由美国西北大学新闻学副教授、新媒体专业主任里奇·戈登提出的,通常用于描述网站上的关键字元数据(标签),或可视化自由格式文本。“词云”就是通过形成“关键词云层”或“关键词渲染”,对网络文本中出现频率较高的“关键词”的视觉上的突出,每个词的重要性以字体大小或颜色显示。今天,我们就一起来学习如何利用Python制作一个简单的智能词云生成器。与上次不同的是,我们这次将给原程序扩展额外功能-从文本文件中提取文本数据(包括纯文本文件(TXT文件),PDF文件,Word文件)。
编程思路:本次编程我们将会调用到调用到wordcloud,PyQt5,pillow(PIL),numpy,sys,os,pytesseract,docx,pdfplumber等库。其中,wordcloud是本次编程的主体,它可以指定词云的形状,颜色,字体,背景等,帮助我们轻松地生成美观、直观的词云。PyQt5被用于创建一个图形用户界面,包括按钮、文本框、标签、布局管理器等组件,以及处理用户交互和事件,它将在词云生成的功能与用户界面相结合方面会很有用。pillow是Python的一个很重要的图像处理库,它被用于读取我们选择好的词云的形状图片,并将其转换为numpy数组,以便在生成词云时使用。numpy则负责进行数据处理(包括我们输入的文本以及传入的图片等信息)。sys和os将会帮助我们与Python解释器(PyCharm)进行交互,以及控制程序的执行,并对系统文件进行相关操作。与上次相比,我们导入了新的库:pytesseract,它可以将图像中的文字转换为字符串,从而实现了对扫描版PDF文件的文字提取。pdfplumber让我们可以很方便地从 PDF 文件中提取文本内容(包括普通的文本型PDF和扫描版的PDF)。docx是Python的一个很强大的文本文件处理库,有了docx 库,我们可以方便地从docx文件(扩展名为.docx)中提取文本内容,并将其显示在文本编辑框中。
第一步:导入库
标准库:sys,os。
第三方库:PyQt5,pillow(PIL),wordcloud,numpy,docx,pdfplumber,pytesseract。
#导入必要库
import sys
import os
import numpy as np
from PIL import Image
from wordcloud import WordCloud
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
import docx
import pdfplumber
import pytesseract
第二步:创建内部功能类
我们用PyQt5库创建了一个功能丰富的GUI类(词云生成器类),允许用户选择形状图片、输入文本内容、选择字体文件、生成词云并保存生成的词云图片。在词云生成器类这个类中,我们创建了总共11个函数(初始化函数,UI初始化函数,系统图片文件夹获取函数,词云形状图片加载函数,字体文件选择函数,词云生成核心逻辑函数和词云图片保存函数等),它们将执行各自的功能,共同完成生成词云图的工作。值得一提的是,相比于上次的代码,本次扩展了4个函数,其功能围绕从文本文件中提取文本数据展开。
#词云生成器类
class WordCloudGenerator(QWidget):
#初始化函数
def __init__(self):
super().__init__()
self.mask_image = None
self.font_path = None
self.temp_path = self.get_pictures_path()
self.initUI()
#UI初始化函数
def initUI(self):
# 主布局
main_layout = QHBoxLayout()
# 左侧面板
left_panel = QVBoxLayout()
# 文件导入按钮
self.import_btn = QPushButton('导入文本文件(txt/docx/pdf(支持OCR识别))', self)
self.import_btn.clicked.connect(self.import_text)
# 形状图片选择
self.image_btn = QPushButton('选择形状图片', self)
self.image_btn.clicked.connect(self.load_image)
# 图片预览
self.image_preview = QLabel('图片预览区域')
self.image_preview.setFixedSize(300, 300)
self.image_preview.setFrameShape(QFrame.Box)
self.image_preview.setAlignment(Qt.AlignCenter)
# 文本输入框
self.text_edit = QTextEdit()
self.text_edit.setPlaceholderText("在此输入文本内容,或使用上方按钮导入文件...")
# 字体选择
self.font_btn = QPushButton('选择字体文件(存在中文字体时需要)', self)
self.font_btn.clicked.connect(self.select_font)
# 生成按钮
self.generate_btn = QPushButton('生成词云', self)
self.generate_btn.clicked.connect(self.generate_wordcloud)
# 保存按钮
self.save_btn = QPushButton('保存词云', self)
self.save_btn.clicked.connect(self.save_wordcloud)
# 添加左侧组件
left_panel.addWidget(self.import_btn)
left_panel.addWidget(self.image_btn)
left_panel.addWidget(self.image_preview)
left_panel.addWidget(QLabel('输入文本:'))
left_panel.addWidget(self.text_edit)
left_panel.addWidget(self.font_btn)
left_panel.addWidget(self.generate_btn)
left_panel.addWidget(self.save_btn)
# 右侧面板
right_panel = QVBoxLayout()
self.result_preview = QLabel('词云预览区域')
self.result_preview.setFixedSize(400, 485)
self.result_preview.setFrameShape(QFrame.Box)
self.result_preview.setAlignment(Qt.AlignCenter)
# 状态栏
self.status_bar = QStatusBar()
# 添加右侧组件
right_panel.addWidget(QLabel('词云图预览:'))
right_panel.addWidget(self.result_preview)
right_panel.addWidget(self.status_bar)
# 组合主布局
main_layout.addLayout(left_panel, 40)
main_layout.addLayout(right_panel, 60)
self.setLayout(main_layout)
# 窗口设置
self.setWindowTitle('智能词云生成器(进阶版)')
self.resize(800, 600)
self.show()
def get_pictures_path(self):
#获取系统图片文件夹路径
if os.name == 'nt':
return os.path.join(os.environ['USERPROFILE'], 'Pictures')
else:
return os.path.expanduser('~/Pictures')
def import_text(self):
#导入文本文件(支持TXT/DOCX/PDF)
path, _ = QFileDialog.getOpenFileName(
self,
'选择文本文件',
'',
'所有支持格式 (*.txt *.docx *.pdf);;文本文件 (*.txt);;Word文档 (*.docx);;PDF文件 (*.pdf)'
)
if path:
try:
text = ""
if path.lower().endswith('.txt'):
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
elif path.lower().endswith('.docx'):
doc = docx.Document(path)
text = '\n'.join([para.text for para in doc.paragraphs])
elif path.lower().endswith('.pdf'):
text = self.extract_pdf_text(path)
else:
QMessageBox.warning(self, '格式错误', '不支持的文本格式!')
return
self.text_edit.setPlainText(text)
QMessageBox.information(self, '导入成功', '文件内容已加载到文本框')
except Exception as e:
QMessageBox.critical(self, '读取错误', f'文件读取失败:{str(e)}')
def extract_pdf_text(self, pdf_path):
#智能PDF文本提取
text = ""
try:
# 先尝试普通文本提取
with pdfplumber.open(pdf_path) as pdf:
total_pages = len(pdf.pages)
for i, page in enumerate(pdf.pages):
self.update_status(f"解析PDF第 {i + 1}/{total_pages} 页...")
page_text = page.extract_text()
text += page_text + "\n" if page_text else ""
QApplication.processEvents()
# 如果文本过少则尝试OCR
if len(text.strip()) < total_pages * 50: # 假设每页至少应有50个字符
self.update_status("检测到扫描版PDF,启用OCR识别...")
return self.extract_scanned_pdf(pdf_path)
return text
except Exception as e:
raise Exception(f"PDF解析失败: {str(e)}")
def extract_scanned_pdf(self, pdf_path):
#处理扫描版PDF(OCR识别)
text = ""
try:
with pdfplumber.open(pdf_path) as pdf:
total_pages = len(pdf.pages)
for i, page in enumerate(pdf.pages):
self.update_status(f"OCR识别第 {i + 1}/{total_pages} 页...")
# 转换为高分辨率图像(300 DPI)
img = page.to_image(resolution=300).original
# 图像预处理
img = Image.fromarray(img).convert('L') # 转为灰度图
# OCR识别
page_text = pytesseract.image_to_string(img, lang='chi_sim+eng')
text += page_text + "\n"
# 保持界面响应
QApplication.processEvents()
return text
except Exception as e:
raise Exception(f"OCR处理失败: {str(e)}")
def update_status(self, message):
#更新状态栏
self.status_bar.showMessage(message)
QApplication.processEvents() # 强制刷新界面
def load_image(self):
#加载形状图片
path, _ = QFileDialog.getOpenFileName(
self, '选择形状图片', '', '图像文件 (*.jpg *.jpeg *.png *.bmp)')
if path:
self.mask_image = np.array(Image.open(path))
pixmap = QPixmap(path).scaled(300, 300, Qt.KeepAspectRatio)
self.image_preview.setPixmap(pixmap)
def select_font(self):
#选择字体文件
path, _ = QFileDialog.getOpenFileName(
self, '选择字体文件', '', '字体文件 (*.ttf *.otf)')
if path:
self.font_path = path
def generate_wordcloud(self):
#生成词云
text = self.text_edit.toPlainText()
if not text.strip():
QMessageBox.warning(self, '内容缺失', '请输入需要生成词云的文本内容!')
return
if self.mask_image is None:
QMessageBox.warning(self, '形状未选', '请先选择词云形状图片!')
return
try:
# 配置词云参数
wc = WordCloud(
background_color='white',
mask=self.mask_image,
font_path=self.font_path,
max_words=200,
width=800,
height=600
)
wc.generate(text)
# 保存临时文件
temp_file = os.path.join(self.temp_path, "wordcloud_temp.png")
wc.to_file(temp_file)
# 显示预览
pixmap = QPixmap(temp_file).scaled(400, 400, Qt.KeepAspectRatio)
self.result_preview.setPixmap(pixmap)
except Exception as e:
QMessageBox.critical(self, '生成错误', f'词云生成失败: {str(e)}')
def save_wordcloud(self):
#保存词云图片
if not self.result_preview.pixmap():
QMessageBox.warning(self, '无内容', '请先生成词云!')
return
default_path = os.path.join(self.temp_path, "我的词云.png")
path, _ = QFileDialog.getSaveFileName(
self,
'保存词云',
default_path,
'PNG图像 (*.png);;JPEG图像 (*.jpg);;所有文件 (*)'
)
if path:
# 保持原始分辨率保存
Image.open(os.path.join(self.temp_path, "wordcloud_temp.png")).save(path)
QMessageBox.information(self, '保存成功', f'词云已保存至:\n{path}')
第三步:创建驱动单元
与上次一样,我们需要创建一段程序,它是Python程序的入口点,我们将利用它来驱动以上词云生成器类并执行相关系统程序。
if __name__ == '__main__':
app = QApplication(sys.argv)
window = WordCloudGenerator()
sys.exit(app.exec_())
第四步:完整代码展示
#导入必要库
import sys
import os
import numpy as np
from PIL import Image
from wordcloud import WordCloud
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
import docx
import pdfplumber
import pytesseract
#词云生成器类
class WordCloudGenerator(QWidget):
#初始化函数
def __init__(self):
super().__init__()
self.mask_image = None
self.font_path = None
self.temp_path = self.get_pictures_path()
self.initUI()
#UI初始化函数
def initUI(self):
# 主布局
main_layout = QHBoxLayout()
# 左侧面板
left_panel = QVBoxLayout()
# 文件导入按钮
self.import_btn = QPushButton('导入文本文件(txt/docx/pdf(支持OCR识别))', self)
self.import_btn.clicked.connect(self.import_text)
# 形状图片选择
self.image_btn = QPushButton('选择形状图片', self)
self.image_btn.clicked.connect(self.load_image)
# 图片预览
self.image_preview = QLabel('图片预览区域')
self.image_preview.setFixedSize(300, 300)
self.image_preview.setFrameShape(QFrame.Box)
self.image_preview.setAlignment(Qt.AlignCenter)
# 文本输入框
self.text_edit = QTextEdit()
self.text_edit.setPlaceholderText("在此输入文本内容,或使用上方按钮导入文件...")
# 字体选择
self.font_btn = QPushButton('选择字体文件(存在中文字体时需要)', self)
self.font_btn.clicked.connect(self.select_font)
# 生成按钮
self.generate_btn = QPushButton('生成词云', self)
self.generate_btn.clicked.connect(self.generate_wordcloud)
# 保存按钮
self.save_btn = QPushButton('保存词云', self)
self.save_btn.clicked.connect(self.save_wordcloud)
# 添加左侧组件
left_panel.addWidget(self.import_btn)
left_panel.addWidget(self.image_btn)
left_panel.addWidget(self.image_preview)
left_panel.addWidget(QLabel('输入文本:'))
left_panel.addWidget(self.text_edit)
left_panel.addWidget(self.font_btn)
left_panel.addWidget(self.generate_btn)
left_panel.addWidget(self.save_btn)
# 右侧面板
right_panel = QVBoxLayout()
self.result_preview = QLabel('词云预览区域')
self.result_preview.setFixedSize(400, 485)
self.result_preview.setFrameShape(QFrame.Box)
self.result_preview.setAlignment(Qt.AlignCenter)
# 状态栏
self.status_bar = QStatusBar()
# 添加右侧组件
right_panel.addWidget(QLabel('词云图预览:'))
right_panel.addWidget(self.result_preview)
right_panel.addWidget(self.status_bar)
# 组合主布局
main_layout.addLayout(left_panel, 40)
main_layout.addLayout(right_panel, 60)
self.setLayout(main_layout)
# 窗口设置
self.setWindowTitle('智能词云生成器(进阶版)')
self.resize(800, 600)
self.show()
def get_pictures_path(self):
#获取系统图片文件夹路径
if os.name == 'nt':
return os.path.join(os.environ['USERPROFILE'], 'Pictures')
else:
return os.path.expanduser('~/Pictures')
def import_text(self):
#导入文本文件(支持TXT/DOCX/PDF)
path, _ = QFileDialog.getOpenFileName(
self,
'选择文本文件',
'',
'所有支持格式 (*.txt *.docx *.pdf);;文本文件 (*.txt);;Word文档 (*.docx);;PDF文件 (*.pdf)'
)
if path:
try:
text = ""
if path.lower().endswith('.txt'):
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
elif path.lower().endswith('.docx'):
doc = docx.Document(path)
text = '\n'.join([para.text for para in doc.paragraphs])
elif path.lower().endswith('.pdf'):
text = self.extract_pdf_text(path)
else:
QMessageBox.warning(self, '格式错误', '不支持的文本格式!')
return
self.text_edit.setPlainText(text)
QMessageBox.information(self, '导入成功', '文件内容已加载到文本框')
except Exception as e:
QMessageBox.critical(self, '读取错误', f'文件读取失败:{str(e)}')
def extract_pdf_text(self, pdf_path):
#智能PDF文本提取
text = ""
try:
# 先尝试普通文本提取
with pdfplumber.open(pdf_path) as pdf:
total_pages = len(pdf.pages)
for i, page in enumerate(pdf.pages):
self.update_status(f"解析PDF第 {i + 1}/{total_pages} 页...")
page_text = page.extract_text()
text += page_text + "\n" if page_text else ""
QApplication.processEvents()
# 如果文本过少则尝试OCR
if len(text.strip()) < total_pages * 50: # 假设每页至少应有50个字符
self.update_status("检测到扫描版PDF,启用OCR识别...")
return self.extract_scanned_pdf(pdf_path)
return text
except Exception as e:
raise Exception(f"PDF解析失败: {str(e)}")
def extract_scanned_pdf(self, pdf_path):
#处理扫描版PDF(OCR识别)
text = ""
try:
with pdfplumber.open(pdf_path) as pdf:
total_pages = len(pdf.pages)
for i, page in enumerate(pdf.pages):
self.update_status(f"OCR识别第 {i + 1}/{total_pages} 页...")
# 转换为高分辨率图像(300 DPI)
img = page.to_image(resolution=300).original
# 图像预处理
img = Image.fromarray(img).convert('L') # 转为灰度图
# OCR识别
page_text = pytesseract.image_to_string(img, lang='chi_sim+eng')
text += page_text + "\n"
# 保持界面响应
QApplication.processEvents()
return text
except Exception as e:
raise Exception(f"OCR处理失败: {str(e)}")
def update_status(self, message):
#更新状态栏
self.status_bar.showMessage(message)
QApplication.processEvents() # 强制刷新界面
def load_image(self):
#加载形状图片
path, _ = QFileDialog.getOpenFileName(
self, '选择形状图片', '', '图像文件 (*.jpg *.jpeg *.png *.bmp)')
if path:
self.mask_image = np.array(Image.open(path))
pixmap = QPixmap(path).scaled(300, 300, Qt.KeepAspectRatio)
self.image_preview.setPixmap(pixmap)
def select_font(self):
#选择字体文件
path, _ = QFileDialog.getOpenFileName(
self, '选择字体文件', '', '字体文件 (*.ttf *.otf)')
if path:
self.font_path = path
def generate_wordcloud(self):
#生成词云
text = self.text_edit.toPlainText()
if not text.strip():
QMessageBox.warning(self, '内容缺失', '请输入需要生成词云的文本内容!')
return
if self.mask_image is None:
QMessageBox.warning(self, '形状未选', '请先选择词云形状图片!')
return
try:
# 配置词云参数
wc = WordCloud(
background_color='white',
mask=self.mask_image,
font_path=self.font_path,
max_words=200,
width=800,
height=600
)
wc.generate(text)
# 保存临时文件
temp_file = os.path.join(self.temp_path, "wordcloud_temp.png")
wc.to_file(temp_file)
# 显示预览
pixmap = QPixmap(temp_file).scaled(400, 400, Qt.KeepAspectRatio)
self.result_preview.setPixmap(pixmap)
except Exception as e:
QMessageBox.critical(self, '生成错误', f'词云生成失败: {str(e)}')
def save_wordcloud(self):
#保存词云图片
if not self.result_preview.pixmap():
QMessageBox.warning(self, '无内容', '请先生成词云!')
return
default_path = os.path.join(self.temp_path, "我的词云.png")
path, _ = QFileDialog.getSaveFileName(
self,
'保存词云',
default_path,
'PNG图像 (*.png);;JPEG图像 (*.jpg);;所有文件 (*)'
)
if path:
# 保持原始分辨率保存
Image.open(os.path.join(self.temp_path, "wordcloud_temp.png")).save(path)
QMessageBox.information(self, '保存成功', f'词云已保存至:\n{path}')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = WordCloudGenerator()
sys.exit(app.exec_())
第五步:运行效果展示
第六步:操作指南
点击"导入文本文件(txt/docx/pdf(支持OCR识别)),然后选择你想要提取的文本数据的文本文件(当然,你也可以手动输入文本数据)。"点击"选择形状图片",选择你想生成的词云图的形状(注意:图片最好分辨率高,以白色为背景为好,这样生成的词云图清晰美观),接着在"输入文本"下面的输入框中输入你想要生成的词云图的内容,接着点击下方的"选择字体文件(存在中文字体时需要)"(注意:如果你的文本数据存在中文文本时需要你自行准备相关的中文字体文件,TTF或OTF都行;如果是纯英文字符的话不用)。再点击下方"生成词云",你就可以在窗口右方看到生成的预览词云图(如果不满意,可以再次点击"生成词云"重新生成词云)。如果满意生成的词云,可以点击下方"保存词云",填写你的词云图片文件名,并选择文件类型(当然,你也可以默认该操作),点击保存,系统会弹出一个信息框,提醒你图片的保存位置,记住这个位置,点击保存。返回计算机系统,按照位置搜索即可找到你生成的词云图。
第七步:温馨提示
1,导入文本文件后文本框中的数据可以手动修改。
2,白色背景的图片可以通过手机"醒图"软件中的"抠图"功能实现。
3,TTF/OTF格式的各种字体文件可以在网站"字体天下"中免费下载。
第八步:注意事项
- 形状图片(建议使用透明背景PNG图片)
- 中文字体文件(如需处理中文内容)
- 首次运行时会在系统图片文件夹创建临时文件
- 保存时可自由修改文件名,但默认保存在系统图片目录
- 程序会自动处理文件夹不存在的情况(系统一般自带Pictures目录)
(我是闪云-微星,感谢你的点赞/关注)