资讯详情

Python词云图绘制指南:中英文文本分词、字体与蒙版实战

📅 2026/10/13 2:00:31 | 华诺云谱 👁 阅读
Python词云图绘制指南:中英文文本分词、字体与蒙版实战
最早接触词云图是在一次分享会上看到别人把一堆演讲稿变成了一张形状鲜明的图高频词像星星一样分布在画面上当时就觉得很酷。后来自己照着Python教程动手做才发现中英文词云图根本不是装个库、传段文本就能出效果那么简单——中文要分词、英文要清洗、字体要折腾、形状要对齐每一步都能卡住新手。这也是我决定把“利用WordCloud绘制中英文词云图”整个自学过程重新梳理一遍的原因希望用最直白的话把从零到能做出像样词云图的路径讲清楚。这篇文章适合三种人一是刚学Python、想做点可视化项目练手的初学者二是需要给报告、课件、文章配图但不想用在线生成器的内容创作者三是已经会用基础绘图、想把词云图的形状和配色玩出花来的进阶选手。我会从原理讲到实操再到我踩过的各种坑基本上你照着敲一遍再自己换文本试几次就能掌握这一整套流程。1. 词云图到底在做什么从文本到视觉的基本盘1.1 词云图的核心逻辑与适用场景词云图在技术上做的其实是一件很朴素的事统计文本中每个词出现的频率然后按照频率高低给词分配大小再把这些词不重叠地排布在一个设定好的区域内。频率高的词字号大、位置更靠近中心或视觉重心频率低的词字号小散落在边缘最终形成一张疏密有致的图。这个逻辑决定了它的适用场景非常明确当你面对的不是结构化数据而是一大段自然语言文本并且你想在几秒钟内让人看出“这段文本到底在反复强调什么”的时候词云图是优于柱状图的。比如分析用户评论、课程反馈、热门文章、工作汇报、聊天记录、简历关键词等一张词云图能把高频主题直接怼到观众脸上。但它也有自己的短板词云图丢失了上下文关系和词之间的语义关联它只能告诉你“哪些词出现了很多次”不能告诉你“这些词是怎么连成句子的”。所以把它当成快速探索文本的起点而不是唯一分析手段才是正确的使用心态。我自己在做文本分析时通常是先用词云图形成直觉再用词频统计和共现矩阵去做深入验证。1.2 工具链选型为什么是WordCloud而不是其他Python生态里能画词云的库不止一个常见的有wordcloud、stylecloud、pyecharts里的词云组件甚至有些图表库也顺带支持。我一开始也纠结过后来统一选择了wordcloud这个基础库原因是它的可控性最强。wordcloud库的设计核心是“词频→大小→排布”所有绘制逻辑都在一个WordCloud类里通过参数控制从字体、颜色、背景到蒙版形状全都可以自定义。相比之下stylecloud虽然几行代码就能出很漂亮的图但它封装得太死想改字间距、想用自定义字体路径、想做精细的中文排版都比较麻烦pyecharts适合在网页上交互展示但生成静态图时反而不如wordcloud直接。选型之前也建议想清楚你的产出形态如果只是临时出图发朋友圈那么任何在线工具都够用根本不用写代码但如果你想批量处理、自动分析、或者把词云图嵌入到自己的数据处理流程里wordcloud就是那个“底子实在”的选项。它不依赖浏览器引擎纯Python实现和pandas、matplotlib、PIL这些常用库配合得都很好。1.3 环境准备与安装Python版本、pip与虚拟环境我的本地环境是Python 3.10wordcloud使用最新稳定版跑目前主流的3.8到3.12都没问题。安装命令很简单pip install wordcloud matplotlib jieba其中wordcloud是核心绘图库matplotlib用于显示图片jieba用来做中文分词。如果你需要读取中文文本文件建议再装一个pandas或者直接用Python内置的open配合编码指定不一定非要pandas。我自己习惯在项目里建一个虚拟环境避免不同项目间的依赖打架python -m venv wordcloud_env # Windows激活 wordcloud_env\Scripts\activate # macOS/Linux激活 source wordcloud_env/bin/activate激活之后再执行pip安装就不会污染全局环境。这里有个容易忽略的点wordcloud依赖了Pillow和numpyPillow负责图像处理numpy负责底层的数组运算安装wordcloud时它们会被自动带上但如果你在本地装了多个Python版本一定要确认你当前使用的是哪个解释器。我之前在Windows上碰到过“pip装上了但运行import wordcloud报ModuleNotFoundError”一查发现是命令行里的pip对应的是另一个Python版本。最简单的解决办法是使用python -m pip install wordcloud保证和当前解释器挂钩。2. 中文词云绕不开的分词不只是空格的事2.1 中文分词原理与jieba用法英文文本按空格和标点切分就行但中文没有天然的分隔符。“我喜欢Python”这句话到底应该切分成“我/喜欢/Python”还是“我/喜欢/Python”不同分词策略结果完全不同。所以中文词云图的核心前置步骤是分词。jieba是目前最常用的中文分词库它基于统计词典和HMM模型提供了三种分词模式精确模式、全模式和搜索引擎模式。做词云图我们只需要精确模式因为它能尽可能把句子切分成最合理的词序列而全模式会把所有可能的词都切出来词云图里会出现大量冗余词。基本用法如下import jieba text 我喜欢用Python做数据可视化 words jieba.lcut(text) # 返回list print(words) # 输出[我, 喜欢, 用, Python, 做, 数据可视化]注意到“数据可视化”这种长度为五个字的词jieba能识别出来是因为它内部维护了一个词典。如果你想让一些专有名词被正确切分可以往词典里添加自定义词语。比如我处理某项目的名称“某跨平台系统”时如果不主动加词它很可能被切成“某/跨/平台/系统”统计出来的词频就乱了。解决办法是先构建一个自定义词典文件或者用add_word临时添加jieba.add_word(跨平台系统)分词之后还需要把结果做成空格分隔的字符串因为wordcloud默认期望接收的是一个字符串而不是词列表。wordcloud内部会自己用空格切分字符串所以如果你直接把词列表传进去它会报错或当成一大串字符处理。2.2 英文文本的预处理细节英文文本相对简单但也不是不处理就能直接上。原始文本里的标点、数字、大小写、原形和变形词都会影响词频的准确性。wordcloud的WordCloud类自带一个process_text方法默认做法是把文本中的非字母字符替换成空格然后按空格切分再统计每个词的出现次数。这个默认行为对纯英文文本已经够用但它不会帮你做词形还原也不会自动区分大小写所以“Python”和“python”会被当成两个不同的词。我在实践中处理英文文本的步骤通常是全部小写避免大小写导致的重复统计去掉标点符号这一步可以用正则也可以用字符串替换去掉数字和网址因为它们往往不是分析目标做词形还原可选用nltk或spacy比如“running”转成“run”免得“run”和“running”被算成两个词最后再用停用词表过滤。注意如果你用了小写转换那么词云图里展示的词就全部是小写视觉上可能不太好看。你也可以在统计完之后再做标题化处理但这会影响词频统计得自己在代码里权衡。2.3 停用词表的重要性与构建方法真实文本里高频出现的往往不是关键词而是“的、了、是、在、我、有、和、就”这类虚词。不把它们去掉词云图会被这些无意义的词占满核心词反而被挤小。所以停用词过滤是中文词云图效果好坏的关键一步。wordcloud本身提供了一个stopwords参数接受一个集合。但它的默认停用词表只包含英文常见词中文的“的、了”不会被自动过滤。网上有很多现成的中文停用词表你可以直接下载但我更建议自己构建一套适合自己项目的停用词表原因是你处理的文本领域不同“无意义词”也不同。比如处理技术类文本时“实现”“进行”“可以”“这个”这类词虽然出现频率很高但对你展示主题没有帮助就可以把它们加入停用词。我在做项目时会把停用词表维护成一个txt文件每行一个词然后加载成setdef load_stopwords(path): with open(path, r, encodingutf-8) as f: return set(line.strip() for line in f)使用的时候在分词后直接过滤words [w for w in jieba.lcut(text) if w not in stopwords and len(w) 1]这里有一个细节如果过滤掉所有单字词词云图会显得更“词块化”因为在中文里单个字往往是虚词或者不成词素的字。但某些场景下单字词也可能是关键词比如“猫”“狗”所以是否保留长度为1的词要视内容而定。我自己一般保留长度大于等于2的词除非某些领域词汇本来就只有一个字。3. 手把手实现一个中英文混合词云图3.1 读取文本与清洗数据中英文混合文本的第一步是读入第二步是清洗。我建议先统一做基础清洗再做分词顺序不要反。这里有一个我常用的清洗函数针对中英文混排场景import re def clean_text(text): # 去掉URL text re.sub(rhttp\S|www\.\S, , text) # 去掉HTML标签 text re.sub(r.*?, , text) # 去掉除了中英文和常用标点之外的内容保留中英文、数字、空格 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9\s], , text) # 将连续的空白替换为单个空格 text re.sub(r\s, , text) return text注意这个函数会去掉所有标点符号包括句号、逗号、引号等。对于词云图来说标点本来就不需要所以可以直接去掉。但如果你的文本里包含英文缩写比如“dont”里的撇号正则里的[^\u4e00-\u9fa5a-zA-Z0-9\s]会把它去掉导致“dont”这种细节在标准词云图里可以接受但如果你对准确性要求高需要单独保留撇号或者用更复杂的规则。我处理时通常会在清洗之后、分词之前把常见的英文缩写替换一下或者直接不洗掉撇号。这个看你的文本情况没有绝对统一的做法。清洗完成之后如果你的文本是混合语言可以先用一个简单方案直接把整个清洗后的字符串传给jieba去分词。jieba对英文是按空白切分的对中文是按词典切分的所以混合段落也能处理。唯一的问题是中英文之间可能出现空格位置不当的情况分词结果里可能会有空字符串过滤掉即可。3.2 配置WordCloud核心参数字体、尺寸、背景与蒙版wordcloud的核心是WordCloud类的参数设置。直接贴一段我日常用的配置from wordcloud import WordCloud wc WordCloud( font_pathpath/to/font.ttf, # 中文字体路径必须设置 width1200, height800, background_colorwhite, max_words300, max_font_size150, min_font_size12, colormapviridis, stopwordsstopwords, random_state42, margin2, prefer_horizontal0.9, maskwordcloud_mask # 可选 )参数的意义逐个解释font_path中文字体路径。这个参数太关键了后面会有专门一节说。width和height画布尺寸单位是像素。尺寸越大生成越慢内存占用越高。1200x800是常用的比例如果你要打印建议至少300dpi那意味着需要把width和height设成3000以上生成时间会明显变长。background_color背景色可以是颜色名或十六进制字符串。注意透明背景要用None而不是rgba。max_words最多显示多少个词词频排序前N个。默认是200效果比较清爽设得太多会显得杂乱。max_font_size和min_font_size控制字号范围。max_font_size不一定要设很大wordcloud会自动根据画布尺寸排版但设了之后能让最大字号更可控。colormap颜色映射默认是浅蓝色单色。可选的值包括viridis、magma、Set2、rainbow等也可以自定义。prefer_horizontal控制词排列方向是横向还是竖直。默认值是0.9意思是90%的词横向10%竖直。如果设成0所有词都是竖直的很多新手把图做成“全是竖排”往往是因为这个参数设成0了。random_state随机种子固定后每次生成的布局一致方便复现。stopwords停用词集合直接传入之前构建的set。3.3 生成与展示matplotlib与PIL的配合生成词云图的核心代码很简单先传入文本调用generate()然后保存或显示wc.generate(source_text) wc.to_file(wordcloud.png)如果使用matplotlib可以这样显示import matplotlib.pyplot as plt plt.figure(figsize(12, 8)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.savefig(wordcloud_show.png, dpi300, bbox_inchestight) plt.show()imshow中的interpolation参数很关键推荐使用bilinear或antialiased能让词云边缘更平滑避免锯齿。如果你不想用matplotlibwordcloud生成的wc对象本身带有to_file方法直接保存PNG即可。另外wordcloud对象内部使用PIL的Image来绘制如果你想把它粘贴到另一张背景图上可以把wc.to_image()转成PIL Image对象再操作。3.4 中文字体缺失问题的解决思路中文字体缺失是新手最容易遇到的拦路虎。如果你不设置font_path生成的词云图里所有中文会变成小方块或者豆腐块。原因是wordcloud默认使用的字体是DroidSansMono这个字体不包含中文字符于是汉字无法渲染只能显示占位符。解决办法是设置一个包含中文的字体文件路径。在Windows上你可以直接用系统的黑体或微软雅黑font_path C:/Windows/Fonts/simhei.ttf或者在macOS上使用苹方或宋体font_path /System/Library/Fonts/PingFang.ttc不过很多同学是Linux服务器环境系统自带的字体目录里可能没有中文字体。我常用的办法是从网上翻一个思源黑体的ttf文件放到项目目录下然后直接写相对路径例如font_path ./fonts/SourceHanSansSC-Regular.otf注意wordcloud对字体格式的支持依赖于PILPIL的ImageFont可以读取ttf、otf、ttc格式但ttcTrueType Collection有时会读取失败尤其是在wordcloud内部解析时。我的经验是尽量用ttf格式实在不行再用otf。如果你运行时报错提示“Invalid path”先检查字体文件是否存在再检查后缀格式。另外不同操作系统对路径分隔符的解析不太一样最好用原始字符串或正斜杠避免反斜杠转义问题。3.5 完整代码中英文混合词云图一步到位把所有环节串起来一个可复用的中英文混合词云图脚本如下import jieba import re from wordcloud import WordCloud, STOPWORDS import matplotlib.pyplot as plt # 1. 读取文本 with open(sample.txt, r, encodingutf-8) as f: whole_text f.read() # 2. 清洗 whole_text re.sub(r.*?, , whole_text) whole_text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9\s], , whole_text) whole_text re.sub(r\s, , whole_text) # 3. 分词 words jieba.lcut(whole_text) # 4. 停用词 stop set(STOPWORDS) stop.update([的, 了, 我, 你, 他, 她, 它, 和, 也, 都, 而, 及, 着, 或者, 就是, 还是, 因为, 所以, 如果, 在, 有, 是, 这, 那, 个, 人, 等]) words [w for w in words if w not in stop and len(w.strip()) 1 and not w.isspace()] # 5. 合并成空格分隔字符串 seg_text .join(words) # 6. 生成词云 wc WordCloud( font_path./fonts/SourceHanSansSC-Regular.otf, width1600, height1000, background_colorwhite, max_words200, colormapcoolwarm, stopwordsstop, random_state42 ).generate(seg_text) # 7. 展示与保存 plt.figure(figsize(16, 10)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.savefig(output.png, dpi300, bbox_inchestight) plt.show()这段代码已经能处理大部分中英文混合文本的场景。我实际测试下来英文字符会以单词为单位被jieba保留中文部分会按照词典切词整体效果不错。但要注意如果你的文本里有英文单词和中文之间没有空格比如“Python编程”jieba可能会把“python”和“编程”分开但有时也会拼在一起。如果你发现某个英文词没被正确分离可以先用正则把中英文交界处加上空格或者使用spaCy等更专业的分词工具这里就不展开了。4. 进阶玩法让词云图真的好看又实用4.1 用蒙版控制词云形状默认生成的词云图是矩形和普通图片没区别。想让词云呈现特定轮廓比如爱心、熊猫、地图或某个人物剪影需要使用蒙版mask。蒙版本质上是一个数组通常是PIL读入图片后转成numpy数组数组中的非零区域就是词云的绘制范围。实现步骤极其简单先用PIL打开形状图片把它转换成numpy数组然后传给WordCloud的mask参数。import numpy as np from PIL import Image mask_image Image.open(mask.png).convert(L) # 转为灰度 mask np.array(mask_image) wc WordCloud(maskmask, ...)注意几个关键细节蒙版图片最好是纯黑白轮廓背景为白色255前景形状为黑色0。因为wordcloud内部会计算mask的非零区域白色区域是背景黑色区域放置词。如果你的图片是彩色或者背景不是纯白可能导致词云填充的区域和预期不符。蒙版图片的尺寸最好和width/height一致或者让wordcloud根据mask自动调整尺寸。实际上当传入mask时wordcloud会以mask数组的形状为准而不是width/height参数。所以width和height可以不用管但mask数组越大生成越慢。蒙版中纯黑色区域如果有小的空洞词可能很难填进去生成的词量会少很多。这时可以适当调整mask图片的阈值或者放宽max_words。这里有一个容易翻车的地方用灰度图时0表示黑色255表示白色。但很多真实图片的背景并不是255可能有阴影或噪点。我会在处理时先做一下阈值二值化mask np.where(mask_image 128, 255, 0).astype(np.uint8)这句话把亮部变成白色暗部变成黑色然后再转成numpy数组给wordcloud。4.2 颜色、配色与自定义调色板默认词云的颜色是一种青色长期看容易审美疲劳。wordcloud提供了colormap参数可以直接用matplotlib的colormap。比如想要暖色调就用Reds或Oranges想要彩虹色就用rainbow。但colormap只能让所有词从同一个色系里取值如果你想根据词频从一种颜色渐变到另一种颜色就需要自定义。wordcloud允许通过color_func参数传入一个函数这个函数接收word, font_size, position, orientation, random_state等参数返回一个颜色值。下面是我常用的颜色函数它让高频词偏暖、低频词偏冷import random def simple_color_func(wordNone, font_sizeNone, positionNone, orientationNone, font_pathNone, random_stateNone): return hsl(%d, 80%%, 50%%) % random.randint(0, 360)直接把函数传给WordCloud(color_funcsimple_color_func)即可。更精细的做法是在函数内部根据font_size映射颜色字体大的词用深红色小的用浅蓝色。这里要注意函数签名里的参数最好都加上默认值因为wordcloud在调用color_func时不一定会传完整参数。还有一种更省事的做法先定义一个Matplotlib colormap然后wordcloud有一个InternalColorFunc可以用ImageColorGenerator从一张图片里提取主色调这样词云图的配色和你的主题图片保持一致。这在做有背景图案的词云时很好看但需要你自己拆解新手可以先不用管。4.3 多文件批量生成与词频统计可视化当你的文本来源不止一个文件时比如要分析一年12个月的用户反馈靠手动改文件名显然不科学。我会写一个简单的循环批量生成多张词云图并且把每一期的词频统计导出成CSV方便后续对比。import pandas as pd files [jan.txt, feb.txt, mar.txt] for name in files: with open(name, r, encodingutf-8) as f: text f.read() # 清洗、分词、过滤... seg_text process_function(text) wc WordCloud(...).generate(seg_text) wc.to_file(name.replace(.txt, _wordcloud.png)) # 获取词频字典 freq wc.words_ df pd.DataFrame(sorted(freq.items(), keylambda x: x[1], reverseTrue), columns[word, count]) df.to_csv(name.replace(.txt, _freq.csv), indexFalse, encodingutf-8-sig)这里有个小知识点WordCloud对象的words_属性是字典键是词值是标准化后的频率0到1之间不是原始出现次数。如果你想保存原始词频需要在分词阶段自己统计Counter。如果你只想看词云图用words_就够了如果要做数据分析还是建议自己用collections.Counter统计原始频次。另外批量生成时要注意font_path、stopwords这些公共参数的复用可以提前写成一份配置文件或常量避免在循环里重复粘贴。我在实际项目中会把整个处理函数封装成一个类维护起来更舒服但对初学者来说先写函数就够了。5. 自学路上我踩过的坑常见问题与排查实录5.1 词汇堆成一团没有形状多半是参数问题同样一段文本别人做出来是错落有致的图案我做出来却是一团糊在一起特别是用蒙版时所有词都集中在中心边缘完全是空的。这个问题在我刚开始做的时候出现过好几次排查下来的原因主要有三个一是mask图的边缘区域太细小单词填不进去二是max_font_size设得过大高频词占据了太多空间三是prefer_horizontal设为1或接近1时词全是横向排列导致高度方向利用率低。解决办法先不换蒙版用默认矩形看词云的分布是否均匀。如果矩形图正常那就说明是蒙版本身的问题可以尝试放大蒙版图上形状的轮廓、加宽线条。如果矩形图也不均匀就调整max_font_size比如从150改成100并把min_font_size调小一些比如8让更多低频词能挤到边缘。另外适当降低max_words也能让整体更清爽。5.2 显示方块或空白字体和编码生成出的词云图里中文全是小方块这个几乎每一个做中文词云图的人都会遇到。原因就是该设置的font_path没设置或者字体路径里的字体不包含中文。我遇到过一种更隐蔽的情况font_path路径里用了相对路径但当前工作目录不是项目目录结果文件找不到。排查方法很简单在设置font_path之前先打印一下import os print(os.path.exists(font_path))如果输出False就改成绝对路径试试。还有一种情况是字体文件本身是中文名称比如“黑体.ttf”在Windows下直接写文件名有时会因为编码问题读取失败改成英文名再试。另外读取文本文件时如果忘记指定encodingutf-8Windows下默认用gbk编码打开utf-8文件会报UnicodeDecodeError。我的习惯是读取任何文本文件时都显式加上encodingutf-8除非明确知道文件是其他编码。5.3 词频数据不准检查分词和停用词当你发现词云图里出现一些明显不对的“词”比如“我们了”“是一个”这种拼接印花大概率是分词结果没有处理好。jieba虽然准确率高但在没有自定义词典的情况下领域专有名词会被切得很碎。我在做某跨平台系统的文本分析时把“跨平台系统”切成了“跨/平台/系统”导致词云里“平台”和“系统”都很大但“跨平台系统”这个整体概念反而没有出现。后来我给jieba加词后就好多了。还有一点停用词表过度过滤会导致“关键词缺失”的反向问题。比如你做的是“数据分析”主题如果把“数据”也加进停用词那词云图里就看不到最重要的词了。所以停用词表要定期复查不能一劳永逸。我的习惯是生成词云图后顺便打印出频率最高的前50个词扫一眼看看是否符合预期能少走很多弯路。5.4 内存溢出和性能优化词云图看似只是画个字但它的算法并不轻。wordcloud内部会计算每个词在当前区域内的最佳排布位置并不断尝试碰撞检测当你把画布尺寸设成5000x5000以上、词数设成500以上时生成时间可能会达到几十秒甚至几分钟内存占用也会飙升。优化思路有三个一是降低画布尺寸先试1200x800确认效果后再放大二是减少max_words比如从300降低到150三是减少蒙版图中白色和黑色交界处的复杂度因为形状边缘越平滑排布时的碰撞计算越少。如果你要生成大量词云图建议使用后台异步任务或者多进程避免阻塞主程序。5.5 常见问题速查表问题场景可能原因快速排查步骤中文变成方块未设置font_path或字体缺字换用中文字体路径检查字体文件是否存在报错ModuleNotFoundErrorPython解释器环境混乱用python -m pip install安装确认当前解释器词云形状不符合maskmask背景不是纯白或mask尺寸过大二值化mask用np.where处理后再传入生成结果太平铺max_font_size太小或margin太大加大max_font_size减小margin词云里全是“的”“了”停用词过滤不彻底加载中文停用词表自己扩充停用集合保存图片模糊分辨率不够提高width/height保存时设dpi300英文字母都挤在一起文本清洗时把空格也删了检查正则保证保留空格分隔符生成特别慢画布尺寸过大、词数过多降低尺寸减少max_words简化mask结束语与一点个人体会折腾词云图的这段时间我最深的感觉是这类可视化工具门槛不高但想做得“严谨”比想象中琐碎。中文分词、停用词选择、字体适配、蒙版处理每一个细节点都会影响最终效果。以前我总觉得词云图就是花架子直到自己亲手把一堆评论变成一张清晰传达核心话题的图时才意识到它其实是文本分析里非常高效的“第一眼工具”。最后再分享一个小技巧在正式生成一张正式词云图之前我一般会先用默认参数快速出一版草稿不急着调蒙版和配色先看分词和词频是否正确。等确认文本处理没问题了再去优化视觉。这一习惯帮我省了大量反复调整的时间。如果你也在用Python折腾词云图强烈建议遵循先“对”再“美”的顺序。希望这篇整理能让你少踩一些我踩过的坑。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑