有趣的便签网站-使用Sdcb.WordCloud生成词云图
<h1 id="有趣的便签网站-使用sdcbwordcloud生成词云图">有趣的便签网站-使用Sdcb.WordCloud生成词云图</h1><h2 id="前言">前言</h2>
<p>最近也是忙着面试、背题,终于闲下来,也是来更新下网站顺便写一篇文章~</p>
<p>上周在网上看到一个静态的便签网站,我也是拿来用并发布了一篇文章,找到一个有趣的便签墙网站 - ZY知识库,后续也是制作了一个可以提交内容的便签网站,也发布了一篇文章,可以提交内容的便签墙来了 - ZY知识库。</p>
<p>这两篇文章都比较火,特别是可以提交内容的便签网站,截止今日,已经有<strong>4000</strong>千多条评论,去除重复的也有<strong>3000</strong>多条,保守估计,网站搭建至今,访问人数和使用人数应该总和有<strong>100+</strong>。</p>
<p>大部分人提交的内容都是表白、鼓励、吐槽之类的内容,也有一些人发布了恶意评论,当然已经被我删除掉了。</p>
<p>需要注意的是,网站<strong>默认</strong>只会展示最新的<strong>150</strong>条信息,如果你发现你之前的信息找不到了,刷新网页好多次还是没看到,那么可能排序到最新的第<strong>150</strong>条数据后面去了。</p>
<p>下面是网站发布以来更新的内容</p>
<ul>
<li>2025/11/04 限制内容长度不能超过30字</li>
<li>2025/11/05 限制 <code>xss</code> 攻击</li>
<li>2025/11/06 高估互联网的素质了,还是加上了关键词过滤</li>
<li>2025/11/07 被恶意刷屏,无奈添加接口限流</li>
</ul>
<p>那么也是为了看看大家都发布的什么内容,也是心血来潮想着用词云图实现一下,通过数据清洗、筛选来看看出现频率最高的词语有哪些,并用 <code>词云图</code> 的形式展示出来,话不多说开始操作。</p>
<p>便签网站直达地址:https://pljzy.top/noteweb</p>
<h2 id="前端设计">前端设计</h2>
<p>前端页面就很简单的设计了一个<strong>词云图</strong>跳转按钮,通过点击这个按钮可以查看词云图。</p>
<p><img src="https://img2024.cnblogs.com/blog/3091176/202511/3091176-20251117134603139-1535283205.png" alt="1" loading="lazy"></p>
<h2 id="后端设计">后端设计</h2>
<p>后端采用的是.Net框架,那么生成词云图必然也是.Net框架下的包,我这里使用的是 <code>Sdcb.WordCloud</code> 包。</p>
<pre><code class="language-csharp">dotnet add package Sdcb.WordCloud
</code></pre>
<p>这个包<strong>Star</strong>数比较少,可能因为词云图本身不是很知名吧,之前绘制词云图还是学校时期用 <code>python</code> 绘制过。不得不说 <code>python</code> 的包是又多又方便~</p>
<p>sdcb/Sdcb.WordCloud: Generate WordCloud image from .NET/.NET Core</p>
<h3 id="踩坑指南">踩坑指南</h3>
<p>我在本地 <code>windows</code> 环境下生成词云图是没问题的,当我部署到 <code>Liunx</code> 系统下时,会提示缺少依赖,后面发现需要手动导入 <code>SkiaSharp.NativeAssets.Linux.NoDependencies</code> 包。</p>
<pre><code class="language-csharp">dotnet add package SkiaSharp.NativeAssets.Linux.NoDependencies
</code></pre>
<h3 id="关键代码">关键代码</h3>
<pre><code class="language-csharp">public async Task GenerateWordCloud()
{
try
{
using var scope = _serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
var allNotes = await db.Notes.Select(n => n.Content).ToListAsync();
var text = string.Join(" ", allNotes);
// 处理文本并生成词频统计
var words = ProcessText(text)
.GroupBy(word => word)
.ToDictionary(g => g.Key, g => g.Count())
.OrderByDescending(kv => kv.Value) // 按词频降序排序
.Take(150) // 取前150个高频词
.ToDictionary(kv => kv.Key, kv => kv.Value)
.Select(kv => new WordScore(kv.Key, kv.Value)); // 转换为WordScore对象集合
// 创建词云实例,设置画布大小为1000x1000
WordCloud wc = WordCloud.Create(new WordCloudOptions(1000, 1000, words));
byte[] pngBytes = wc.ToSKBitmap().Encode(SKEncodedImageFormat.Png, 100).AsSpan().ToArray();
string filePath = "wwwroot/wordcloud.png";
// 将PNG图片保存到文件
await File.WriteAllBytesAsync(filePath, pngBytes);
_logger.LogInformation($"词云图已更新: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
}
catch (Exception ex)
{
_logger.LogError(ex, "生成词云图时发生错误");
}
}
</code></pre>
<p>这里由于各方面因素影响,只获取了词频出现最多的前 <code>150</code> 条数据。</p>
<h3 id="定时任务">定时任务</h3>
<p>由于词云图生成比较慢,我试了一下150条数据,生成大概要 <code>20~30s</code> ,如果做成接口的话响应很慢,用户体验也不好。</p>
<p>那么我也是采用后台定时任务去执行,我使用的是 <code>Hangfire</code> ,每隔<strong>4小时</strong>重新绘制一次图像。</p>
<pre><code class="language-csharp">dotnet add package Hangfire
dotnet add package Hangfire.MemoryStorage
</code></pre>
<h4 id="关键代码-1">关键代码</h4>
<pre><code class="language-csharp">builder.Services.AddHangfire(config =>
config.UseMemoryStorage());
builder.Services.AddHangfireServer();
// 每天0:00, 4:00, 8:00, 12:00, 16:00, 20:00执行
RecurringJob.AddOrUpdate<WordCloudService>(
"generate-wordcloud",
service => service.GenerateWordCloud(),
"0 0,4,8,12,16,20 * * *");
</code></pre>
<h4 id="多提一嘴">多提一嘴</h4>
<p><code>Hangfire</code> 可以选择是否开启仪表板,仪表板截图如下,用来查看任务执行情况还是挺方便的。</p>
<p><img src="https://img2024.cnblogs.com/blog/3091176/202511/3091176-20251117134621714-1369772502.png" alt="2" loading="lazy"></p>
<h2 id="词云图效果">词云图效果</h2>
<p>好像大家都有喜欢的人呀,词云图每隔4小时更新一次,如果发现自己发的文字没有在里面可能是出现频率不高,不过不建议大家刷留言~</p>
<p><img src="https://img2024.cnblogs.com/blog/3091176/202511/3091176-20251117134636477-1078278325.png" alt="3" loading="lazy"></p>
<h2 id="结尾">结尾</h2>
<p>如果对项目源代码感兴趣的可以访问 <code>Github</code> 并点上 <code>Star</code> ,ZyPLJ/NoteWeb: 便签墙带后端版本,可以随心所欲(注意文明用语)的发送便签~。</p>
<p>这个项目也就到这里结束了,后续不会更新内容,网站会一直存在,除非服务器到期和其他不可避免因素导致网站关闭。</p>
<p>总的来说,这是一个很有趣的项目,希望能一直存在下去~</p>
<h2 id="参考链接">参考链接</h2>
<ul>
<li>一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云</li>
</ul><br><br>
来源:https://www.cnblogs.com/ZYPLJ/p/19232405
頁:
[1]