Python词云实战:wordcloud+jieba分词与蒙版上色

发布时间:2026/11/6 5:49:18
Python词云实战:wordcloud+jieba分词与蒙版上色 简介这是一份面向文本挖掘与数据可视化初学者、中级 Python 用户的词云实战教程 PDF适合在项目报告、数据分析、社交媒体监控与教学演示中快速应用词云。内容围绕 wordcloud 与 matplotlib 展开先说明词云的定义、作用、生成原理与文本预处理、词频/TF-IDF 权重计算、展示保存流程再以 7 个递进案例演示最简调用、中文不分词、中文分词、蒙版形状、自定义配色及关键词与权重提取等做法每个案例配有代码与运行结果并给出 PyCharm、Python 3.7、wordcloud 1.9.3、matplotlib 3.5.3 的环境参考。资源包为 1 个 PDF 文件大小约 2.07MB目录紧凑便于按案例查阅和动手复现。已有 109 人学习适合希望从零掌握词云生成并迁移到实际文本分析场景的读者。1. 别人交一张图你交一份能复现的流程做课程作业或者内部小分享时最常被派到的一类活儿就是把一堆文字变成一张一眼能看懂的图。python 词云几乎是性价比最高的选择不用碰模型wordcloud 加上 matplotlib 二十来行就能出图。这份 python 课程资源里的七个案例就是照着这条线走的从三行最简调用到中文分词、蒙版形状、蒙版取色、自定义颜色函数最后落到 jieba 的关键词权重上。适合谁会一点 python 语法、想把文本可视化塞进报告里的初学者也适合那些已经会抄WordCloud()但每次都被中文方块、空白图、形状反了折腾半天的人。七个案例串起来其实是一条完整链路文本 → 分词 → 权重 → 布局 → 上色 → 落盘把这条链路上的每一环拆开看比收藏十个零散片段管用。2. 环境与选型wordcloud、matplotlib、jieba 三件套怎么搭教程给的环境很明确PyCharm 编辑器Python 3.7wordcloud 1.9.3matplotlib 3.5.3。这个组合不是随便写的wordcloud 早期版本在 Windows 上装起来相当玄学得现装 C 编译工具链1.9.x 之后官方 wheel 覆盖得比较全Python 3.7 到 3.11 基本都能直接pip install装好。真正决定成败的不是版本号本身而是三件事字体文件能不能被找到、蒙版图能不能被正确转成数组、jieba 分出来的词是不是你想要的那些词。把这三点在环境阶段准备好后面七个案例改起来才顺手。2.1 依赖清单与安装顺序wordcloud 依赖 numpy 和 pillowmatplotlib 自己带一套后端依赖jieba 是纯 Python 无编译依赖。安装顺序其实无所谓pip 会自己解依赖但 numpy 跟着 wordcloud 一起装的时候容易拿到最新版而在 Python 3.7 上最新版 numpy 可能不兼容所以更稳的做法是先锁版本。# 建一个干净环境别和系统里那堆包混着 python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate # 按教程版本装numpy 手动降一档避免 3.7 上的兼容问题 pip install numpy1.22 pillow pip install wordcloud1.9.3 matplotlib3.5.3 jiebavenv这一步很多人省掉结果是跑案例 01 时报ImportError: cannot import name WordCloud其实是装了个同名但残缺的包或者装到了另一个解释器里。PyCharm 里对应要在 Settings → Project Interpreter 指向venv/bin/python不要用系统自带的那个。装完可以在解释器里敲import wordcloud; print(wordcloud.__version__)输出版本号才是真装上了。如果你本地是 Python 3.11 也不用退回 3.7wordcloud 1.9.3 有对应的 wheel只是 matplotlib 可以上 3.7 以上。2.2 中文字体最容易翻车的一环七个案例里有六个跟中文有关而 wordcloud 默认用的字体是 DroidSansMono里面没有汉字字形出来的图就是一堆方块。教程里给了一份随包的 ttf 字体并在代码里用font_path../Hiragino.ttf指向它你本地换成系统里的中文字体效果一样。Linux 上常见的是wqy-zenhei.ttc或NotoSansCJK-Regular.ttcWindows 上是C:/Windows/Fonts/simhei.ttf或msyh.ttcmacOS 上是/System/Library/Fonts/PingFang.ttc。不知道本机有哪些可选字体可以用 matplotlib 的字体管理器扫一遍比翻目录快import matplotlib.font_manager as fm # 扫出系统里名字带黑体/宋体/雅黑关键词的字体文件 cands [f.fname for f in fm.fontManager.ttflist if any(k in f.name for k in (Hei, Song, YaHei, PingFang, Noto))] for p in dict.fromkeys(cands): # 去重后打印 print(p)打印出来的路径可以直接丢给font_path。要注意.ttc是字体集合文件wordcloud 对它的支持依赖 pillow 版本遇到OSError: cannot open resource就换成同目录下的.ttf。我一般会把选定的字体路径写成一个模块级常量FONT七个案例共用改一处全改完比在每个文件里写死路径省事。2.3 素材目录一次摆好案例直接跑教程里的代码用了../xyj.txt、../black_mask.png、../color_mask.png这种相对路径说明原始工程是「代码放子目录、素材放上级目录」的结构。自己复现时不必照抄但建议保持「素材和输出分开」这个习惯否则跑几轮下来目录里全是 png。文件作用说明text_cn.txt中文样本文本几万字为宜UTF-8 编码text_en.txt英文样本文本案例 01 用空格天然分词stopwords.txt停用词表一行一个词可自己增删black_mask.png黑白形状蒙版白底黑形决定字的落点color_mask.png彩色蒙版既定形状又定颜色out/输出目录to_file统一写这里蒙版图建议控制在 1000×1000 像素以内。两三千像素的大图放进mask参数wordcloud 内的碰撞检测复杂度会上去一张图跑几十秒很常见这个坑后面还会细说。3. 文本处理链路jieba 分词、停用词与关键词权重七个案例里真正拉开差距的不是画图那几行而是「交给 wordcloud 的到底是什么」。案例 01 和 02 给的是原始文本案例 03 之后给的是分词结果案例 07 给的干脆是{词: 权重}字典。三种输入对应 wordcloud 的两个入口generate(text)和generate_from_frequencies(freq)。把这条输入侧链路搞明白七个案例基本就通了。3.1 案例 01 与 02最简调用和中文不分词案例 01 是标准的英文路径读文件、生成对象、显示、保存四步。这段代码短到没什么可优化但它是验证环境是否装好的最快方式——能出图说明 wordcloud、matplotlib、字体、样本文件四样都对了。# -*- coding: utf-8 -*- from wordcloud import WordCloud import matplotlib.pyplot as plt # 读文本显式指定编码避免 Windows 下 GBK 报错 with open(text_en.txt, r, encodingutf-8) as f: text f.read() # 默认参数先跑通不传 font_path 时用内置英文字体 wc WordCloud().generate(text) plt.imshow(wc, interpolationbilinear) # bilinear 让缩放更平滑 plt.axis(off) # 关掉坐标轴出图更干净 plt.show() wc.to_file(out/wordcloud.png)interpolationbilinear是 matplotlib 的插值方式词云本身是位图放大显示时用双线性插值不会出现锯齿块。plt.axis(off)不去掉的话图四周会带着刻度线和边框直接放进 PPT 很难看。wc.to_file()这一步和plt.show()是两条独立的输出路径前者按 wordcloud 对象自身的尺寸存原图后者受窗口缩放影响想要高清图以to_file为准或者在构造时加scale2。案例 02 把文本换成了中文并加了font_path、width、height、mode、background_color五个参数但没做分词。这一版的实际效果通常不理想wordcloud 内部靠正则把文本切成 token中文句子没有空格很容易整段连续汉字被当成一个超长 token最后图上只有寥寥几个巨大的词块甚至一片空白。# 中文不分词能出图但词块数量往往少得可怜 wc WordCloud(font_pathHiragino.ttf, width800, height600, modeRGBA, background_colorNone).generate(text)modeRGBA配上background_colorNone是让背景透明的组合存成 png 后可以直接贴到别的底色上。这个组合在案例 02 到 07 里反复出现也埋了个坑很多看图软件在白色背景上显示透明图看着像「图没出来」后面排查章节会专门讲。3.2 案例 03jieba 分词把长句拆成词案例 03 只加了两行却是整个教程的分水岭import jieba with open(text_cn.txt, r, encodingutf-8) as f: text f.read() # cut 返回生成器用空格 join 成字符串喂给 wordcloud text .join(jieba.cut(text)) print(text[:100]) # 打印前 100 字符肉眼确认分词是否正常 wc WordCloud(font_pathFONT, width800, height600, modeRGBA, background_colorNone).generate(text)jieba.cut(text)默认是精确模式尽量把句子切成最合理的词cut(text, cut_allTrue)是全模式会把「中华人民共和国」同时切出「中华」「华人」「人民」「共和国」词多但噪声大jieba.cut_for_search(text)是搜索引擎模式在精确模式基础上把长词再切一刀召回高、适合建索引做词云反而容易把长词的权重摊薄。做词云我一般就用默认的精确模式稳。 .join(...)这一步不能省。wordcloud 的分词逻辑认空格jieba 返回的是生成器直接塞进去会报类型错误即使转成列表传进去中间没有空格分隔等于没分词。print(text[:100])这行看着随意实际是最高效的自检手段分词结果不对一眼就能看出来。3.3 停用词表与自定义词典教程的七个案例都没处理停用词但真实文本里「的」「了」「是」「我们」这类词频极高不滤掉的话它们会霸占词云里最大的几块位置图就废了。常见做法是准备一份一行一个词的 txt读成集合后在分词阶段过滤import jieba def load_stopwords(pathstopwords.txt): with open(path, r, encodingutf-8) as f: return {line.strip() for line in f if line.strip()} STOP load_stopwords() def cut_and_clean(text, min_len2): words [] for w in jieba.cut(text): w w.strip() # 过滤空白、停用词、单字和纯标点 if not w or w in STOP or len(w) min_len: continue if not w.isalnum() and len(w) 2: continue words.append(w) return .join(words)min_len2是为了滤掉单字。中文单字在词云里几乎没有信息量全是「的」「了」「着」这种高频虚词留着只会让图变成噪点集合。w.isalnum()那个判断是兜底把「」「。」这类标点挡掉。另一半是个性化词典。人名字、产品名、行业术语在 jieba 的通用词典里往往被切碎比如某个四字的产品名被拆成两个两字词。这时候用jieba.add_word或load_userdict补进去jieba.load_userdict(userdict.txt) # 一行一个词可带词频和词性 jieba.add_word(词云可视化, freq200, tagnz) # 临时补一个词freq是词频值越大越不容易被切开tag是词性标注做关键词提取时会影响结果。加完再统计一次词频能看到目标词从两个碎片变成一个完整词条这一步的收益比调颜色参数大得多。3.4 案例 07从词频到关键词权重案例 07 换了输入方式不再传原始文本而是先算出{词: 权重}再交给generate_from_frequenciesimport jieba.analyse # topK 取前 200 个关键词withWeight 让返回值带上 TF-IDF 权重 freq jieba.analyse.extract_tags(text, topK200, withWeightTrue) print(freq[:20]) # 先看前 20 个确认关键词合理 freq {i[0]: i[1] for i in freq} # 列表转字典符合接口要求 wc WordCloud(maskmask, font_pathFONT, modeRGBA, background_colorNone).generate_from_frequencies(freq)extract_tags底层用的是 TF-IDF一个词在本篇里出现得多、在整个语料里出现得少权重就高。topK200控制候选词数量太大图会挤成一团太小又显得空200 个是个常见起点。withWeightTrue不传的话返回的是纯词列表没法直接喂给generate_from_frequencies。generate_from_frequencies和generate的关键差别在于前者完全跳过分词和统计字的大小直接由你给的数值决定也不受max_words之外的因素干扰后者内部要先切词再统计词频。当你想精确控制「哪些词上榜、谁大谁小」时走 frequencies 这条路更可控比如做两个时期的对比图可以把两边的权重字典合并后统一生成避免两次随机布局导致视觉上不可比。4. 出图链路mask 蒙版、ImageColorGenerator 与 color_func案例 04 到 06 三个案例代码只差几行但它们是整个教程里最容易被抄错的部分——因为蒙版的语义是反直觉的颜色的来源也不止一种。把这三个案例放在一起看其实是同一套机制的三层先定形状再定颜色来源最后完全自定义颜色。4.1 案例 04mask 的数组语义import numpy as np from PIL import Image mask np.array(Image.open(black_mask.png)) wc WordCloud(maskmask, font_pathFONT, modeRGBA, background_colorNone).generate(text)mask要的是 numpy 数组不是图片路径。Image.open读进来是 PIL 对象必须np.array转一道直接传图片对象会报属性错误。关键在于黑白谁填字。wordcloud 内部会把数组里等于 255 的位置视为「不可放置区」也就是说白色区域不放词黑色区域才是字的落点。这和大多数人的直觉相反——做形状蒙版时很多人画了一个黑色底、白色主体形状的图结果出来的是「背景上有一圈字、形状里空的」。正确做法是把主体形状涂黑背景留白。案例里给的black_mask.png就是按这个规则做的。# 快速自检蒙版看尺寸、类型和白色像素占比 print(mask.shape, mask.dtype, (mask 255).mean())(mask 255).mean()输出去重后白色像素的比例。这个值在 0.3 到 0.7 之间比较理想太小说明可放字区域太窄词会挤在一起太大说明形状几乎不存在跟不传 mask 差不多。dtype最好是uint8如果是 RGBA 四通道wordcloud 只看前三个通道一般也能用但保险起见可以Image.open(...).convert(RGB)再转数组。尺寸也在这里看一眼。蒙版宽度超过 2000 像素时生成时间会明显拉长因为布局算法要在这么大的网格上做碰撞检测。常见做法是先缩到 800 到 1200 之间再算出图需要高清的话用scale2参数在最终渲染时放大而不是靠蒙版本身撑大。4.2 案例 05ImageColorGenerator 从蒙版图里取色from wordcloud import ImageColorGenerator mask np.array(Image.open(color_mask.png)) wc WordCloud(maskmask, font_pathFONT, modeRGBA, background_colorNone).generate(text) # 用蒙版图自身的颜色给词上色 image_colors ImageColorGenerator(mask) wc.recolor(color_funcimage_colors)ImageColorGenerator的逻辑是对每个词所在的矩形区域取蒙版图对应位置的平均颜色用它给这个词上色。这意味着蒙版图本身就是调色板——想做成暖色系就把蒙版画成红橙黄想做成品牌色就把蒙版填成对应的几个色块。这也是案例 05 和案例 04 最大的区别同样是形状彩色蒙版额外承担了配色的职责。recolor是原地修改没有返回值不需要wc wc.recolor(...)。这一点抄错了不会报错但会得到一个None后面to_file直接崩。顺序也不能反必须先generate再recolor因为上色时要知道每个词落在哪个位置。有一个细节蒙版里有大量纯黑像素时取出来的平均色会偏暗词看起来很脏。常见做法是在蒙版里避免使用纯黑改用深灰或者饱和度高一点的颜色。4.3 案例 06自定义颜色函数import random def random_color(word, font_size, position, orientation, font_path, random_state): # HSL 里写死色相 0红饱和度和亮度随机整体是同一色系 s hsl(0, %d%%, %d%%) % (random.randint(60, 80), random.randint(60, 80)) print(s) # 调试期看每次生成的颜色 return s wc WordCloud(color_funcrandom_color, maskmask, font_pathFONT, modeRGBA, background_colorNone).generate(text)这个函数每个词调一次六个参数分别是词本身、字号、在画布上的坐标、旋转角度、字体路径和一个随机状态。函数只要能返回一个 matplotlib 认识的颜色字符串就行hsl(0, 70%, 60%)、#ff6600、(0.2, 0.4, 0.9)都可以。教程里把色相写死成 0只让饱和度和亮度波动得到的是同色系深浅不一的红视觉上很统一。想看多色就把色相也放开hsl(%d, 70%%, 60%%) % random.randint(0, 360)。参数个数跟着 wordcloud 版本会微调1.9.3 调用时还会额外传**kwargs稳妥写法是给函数加一个**kwargs兜底不然升级版本就报TypeError。顺带说一句print(s)这种调试输出放在生成几千个词的场景里会拖慢速度确认效果后记得删掉。用random_state参数固定随机种子可以让颜色可复现做对比图的时候很有用。5. 避坑与排查跑不起来、出图空白、中文变方块这七个案例的代码量都不大但踩坑率相当高因为错误大多不报异常只是安静地给你一张错图。下面几条是我自己复现时反复撞上的按「现象 → 原因 → 解决」列出来对照着排查能省不少时间。5.1 生成阶段的问题现象一整张图空白或者只有零星几个巨大的词块。原因通常是中文没分词。wordcloud 内部按正则切 token连续汉字容易被当成一个超长字符串导致候选词只有几个布局算法把它们放大后铺满画布。另一个可能是一次性塞进去的文本太长又没设max_words词太多互相挤不下最后只成功放上去几个。 解决是先print(text[:100])看一眼喂进去的字符串有没有空格分隔没有就补 .join(jieba.cut(text))再把max_words显式设成 200 左右配合min_font_size8防止小词被裁掉。现象二中文全是方块或者干脆不显示。原因就一个font_path指向的字体里没有汉字字形或者路径写错但被静默忽略了。相对路径在不同工作目录下会指向不同位置从 PyCharm 直接运行时工作目录是项目根双击脚本运行时是脚本所在目录行为不一致。 解决是用绝对路径或者在启动时os.chdir(os.path.dirname(os.path.abspath(__file__)))固定工作目录。验证办法是先单独用这个字体渲染一个中文字符串看看能显示再往下走。现象三报ValueError: We need at least 1 word to plot a word cloud。原因是大面积停用词过滤加最短长度限制之后候选词被清空了也可能是读文件时编码不对读进来一堆乱码分词后没有有效词条。 解决是先把过滤关掉跑一遍原始文本确认能出图后再逐层加过滤条件读文件统一encodingutf-8遇到 GBK 文件用errorsignore兜底。5.2 显示与保存阶段的问题现象四to_file存出来的图打开是空白但plt.show()明明能看到。原因是modeRGBA加background_colorNone生成的是透明背景图很多看图软件默认白底透明区域里的浅色字在白底上完全看不见视觉上就是一片白。 解决是看图软件里换成深色背景预览或者临时把background_color设成white调试一轮确认字形布局正常后再改回透明。真正交付时透明图放深色 PPT 上反而更好看所以不是参数错了是预览方式的问题。现象五形状蒙版出来的字全在背景上形状内部是空的。原因是蒙版黑白搞反了。前面说过wordcloud 把 255白视为不可放置区只有非白区域才落字。 解决是在图像工具里把主体形状填黑、背景留白或者在代码里做一次反转mask 255 - mask但要注意彩色蒙版不能这么取反会破坏配色。现象六脚本跑完没反应窗口不弹出来。原因常见于在无图形界面的环境里执行或者plt.show()后面还有代码但被阻塞住了。matplotlib 的默认后端在服务器上是 Aggshow()什么都不会发生。 解决是改用plt.savefig()或直接wc.to_file()落盘不依赖窗口。跑批处理脚本时把plt.show()全部注释掉只保留存图那一行。6. 二次开发把七个案例收成一个可复用的生成器七个案例跑通之后下一步大概率是要在项目里反复用这时候把它们复制粘贴到各处就很痛苦了。我的做法是抽一个函数把「文本清洗 → 分词 → 权重 → 蒙版 → 上色 → 落盘」封进一条链路七个案例变成它的不同参数组合import os, jieba, numpy as np from PIL import Image from wordcloud import WordCloud, ImageColorGenerator def build_cloud(text, outout/wc.png, fontsimhei.ttf, mask_pathNone, stopwordsNone, use_tfidfFalse, topk200, seed42): # 1. 过滤停用词和单字 words [w for w in jieba.cut(text) if w.strip() and len(w) 1 and w not in (stopwords or set())] clean .join(words) mask np.array(Image.open(mask_path).convert(RGB)) if mask_path else None wc WordCloud(font_pathfont, maskmask, modeRGBA, background_colorNone, max_wordstopk, random_stateseed, scale2) # 2. 两条输入路径原始文本 或 关键词权重 if use_tfidf: freq dict(jieba.analyse.extract_tags(clean, topKtopk, withWeightTrue)) wc.generate_from_frequencies(freq) else: wc.generate(clean) # 3. 有彩色蒙版就按图取色 if mask_path: wc.recolor(color_funcImageColorGenerator(mask)) os.makedirs(os.path.dirname(out), exist_okTrue) wc.to_file(out) return outrandom_state固定成 42 是我加后悔药的习惯词云布局本身带随机性同一个文本跑两次形状不一样做前后对比图时很要命。固定种子之后改动只体现在数据上视觉差异能直接归因。scale2让输出放大一倍配合前面把蒙版压到 1000 像素以内的做法等于「小画布算得快、大倍数存得清」。几个常用参数组合对应的就是原来的七个案例调用方式等价案例适用场景use_tfidfFalse, mask_pathNone案例 01/03快速看词频分布mask_pathblack_mask.png案例 04只要形状不要花色mask_pathcolor_mask.png案例 05形状和配色都来自图use_tfidfTrue, mask_path...案例 07报告配图突出核心议题再往下做可以加一层命令行入口把这些参数暴露成--text、--mask、--topk、--tfidf几个开关跑批时用 shell 循环喂文件比改代码快得多。另一个值得做的扩展是加时间维度把不同月份的权重字典分别算出来用同一张蒙版和同一个random_state生成一组图放在一起能看出关键词的迁移这比单张词云的信息量大得多。血泪经验是别在封装的时候顺手把plt.show()也包进去。批处理跑十几个文件每个都弹一个窗口得一个个手动关中途还容易卡死。存图归存图显示归显示两件事分开。从那以后我每次封这类工具函数都会先把「有没有交互式窗口」这一条强制过一遍确认纯命令行也能跑完再往项目里放。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询