- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
⭐️基础链接导航⭐️ 。
服务器 → ☁️ 阿里云活动地址 。
看样例 → 🐟 摸鱼小网站地址 。
学代码 → 💻 源码库地址 。
大家好呀,我是summo,最近小网站崩溃了几天,原因一个是SSL证书到期,二个是免费的RDS也到期了,而我正边学习边找工作中,就没有顾得上修,不好意思哈(PS:八股文好难背,算法好难刷).
小网站的内容和组件也不少了,今天我们继续来丰富的它的功能,让它看起来更美观和有用。今天会增加词云组件和搜索组件,并且还会将网站的内容排列一下,难度不高,但是更有意思。我们先从词云组件开始做.
不同机构的热搜有一样也有不一样的,词云组件的作用是将热搜标题进行分词和计数,统计出最高频率的热搜,方便大家快速了解最热的热搜内容是什么.
jieba是一个分词器,可以实现智能拆词,最早是提供了python包,后来由花瓣(huaban)开发出了java版本。 源码连接:https://github.com/huaban/jieba-analysis 。
<!-- jieba分词器 -->
<dependency>
<groupId>com.huaban</groupId>
<artifactId>jieba-analysis</artifactId>
<version>1.0.2</version>
</dependency>
Demo如下:
package com.summo.sbmy.web.controller;
import com.google.common.collect.Lists;
import com.huaban.analysis.jieba.JiebaSegmenter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class WordCloudTest {
public static void main(String[] args) {
List<String> titleList = Lists.newArrayList(
"《花100块做个摸鱼小网站! 》第七篇—谁访问了我们的网站?",
"《花100块做个摸鱼小网站! 》第六篇—将小网站部署到云服务器上",
"《花100块做个摸鱼小网站! 》第五篇—通过xxl-job定时获取热搜数据",
"《花100块做个摸鱼小网站! 》第四篇—前端应用搭建和完成第一个热搜组件",
"《花100块做个摸鱼小网站! 》第三篇—热搜表结构设计和热搜数据存储",
"《花100块做个摸鱼小网站! 》第二篇—后端应用搭建和完成第一个爬虫",
"《花100块做个摸鱼小网站! 》第一篇—买云服务器和初始化环境",
"《花100块做个摸鱼小网站! · 序》灵感来源");
JiebaSegmenter segmenter = new JiebaSegmenter();
Map<String, Integer> wordCount = new HashMap<>();
Iterator<String> var4 = titleList.iterator();
while (var4.hasNext()) {
String title = var4.next();
List<String> words = segmenter.sentenceProcess(title.trim());
Iterator<String> var7 = words.iterator();
while (var7.hasNext()) {
String word = var7.next();
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
}
wordCount.forEach((word, count) -> {
System.out.println("word->" + word + ";count->" + count);
});
}
}
运行结果如下:
从结果上看,句子已经被分成多个词语,并且统计出了次数,但是还出现了很多无意义的词语,比如“的”、“和”、“了”这些,这样的词语被称为停用词,一般这样的词要过滤掉。我们可以去网上搜索常见的停用词,然后在设置权重的时候把它给剔除掉。我使用的停用词库已经提交到了代码库中,大家可以直接取用.
WordCloudController.java 。
package com.summo.sbmy.web.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Sets;
import com.huaban.analysis.jieba.JiebaSegmenter;
import com.summo.sbmy.cache.hotSearch.HotSearchCacheManager;
import com.summo.sbmy.cache.sys.SysConfigCacheManager;
import com.summo.sbmy.common.model.dto.HotSearchDTO;
import com.summo.sbmy.common.model.dto.WordCloudDTO;
import com.summo.sbmy.common.result.ResultModel;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/hotSearch/wordCloud")
public class WordCloudController {
private static Set<String> STOP_WORDS;
private static JSONArray WEIGHT_WORDS_ARRAY;
@RequestMapping("/queryWordCloud")
public ResultModel<List<WordCloudDTO>> queryWordCloud(@RequestParam(required = true) Integer topN) {
List<HotSearchDTO> hotSearchDTOS = gatherHotSearchData();
List<String> titleList = hotSearchDTOS.stream().map(HotSearchDTO::getHotSearchTitle).collect(Collectors.toList());
return ResultModel.success(findTopFrequentNouns(titleList, topN));
}
/**
* 获取停用词
*
* @return
*/
private List<HotSearchDTO> gatherHotSearchData() {
String stopWordsStr = SysConfigCacheManager.getConfigByGroupCodeAndKey("WordCloud", "StopWords");
STOP_WORDS = Sets.newHashSet(stopWordsStr.split(","));
WEIGHT_WORDS_ARRAY = JSONArray.parseArray(SysConfigCacheManager.getConfigByGroupCodeAndKey("WordCloud", "WeightWords"));
List<HotSearchDTO> hotSearchDTOS = new ArrayList<>();
HotSearchCacheManager.CACHE_MAP.forEach((key, detail) -> {
hotSearchDTOS.addAll(detail.getHotSearchDTOList());
});
return hotSearchDTOS;
}
/**
* 分词
*
* @param titleList 标题列表
* @param topN 截取指定长度的热词大小
* @return
*/
public static List findTopFrequentNouns(List<String> titleList, int topN) {
JiebaSegmenter segmenter = new JiebaSegmenter();
Map<String, Integer> wordCount = new HashMap<>();
Iterator<String> var4 = titleList.iterator();
while (var4.hasNext()) {
String title = var4.next();
List<String> words = segmenter.sentenceProcess(title.trim());
Iterator<String> var7 = words.iterator();
while (var7.hasNext()) {
String word = var7.next();
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
}
return wordCount.entrySet().stream()
//停用词过滤
.filter(entry -> !STOP_WORDS.contains(entry.getKey()))
//构建对象
.map(entry -> WordCloudDTO.builder().word(entry.getKey()).rate(entry.getValue()).build())
//权重替换
.map(wordCloudDTO -> {
if (CollectionUtils.isEmpty(WEIGHT_WORDS_ARRAY)) {
return wordCloudDTO;
} else {
WEIGHT_WORDS_ARRAY.forEach(weightedWord -> {
JSONObject tempObject = (JSONObject) weightedWord;
if (wordCloudDTO.getWord().equals(tempObject.getString("originWord"))) {
wordCloudDTO.setWord(tempObject.getString("targetWord"));
if (tempObject.containsKey("weight")) {
wordCloudDTO.setRate(tempObject.getIntValue("weight"));
}
}
});
return wordCloudDTO;
}
})
//按出现频率进行排序
.sorted(Comparator.comparing(WordCloudDTO::getRate).reversed())
//截取前topN的数据
.limit(topN)
.collect(Collectors.toList());
}
}
这里我加了一个权重替换的逻辑,因为我发现分词器对于有些热词的解析有问题。比如前段时间很火的热搜“黑神话-悟空”,但在中文里面“黑神话”并不是一个词语,所以结巴在分词的时候只能识别“神话”这个词。为了解决这样的问题,我就加了一个手动替换的逻辑.
组件官方文档链接如下:https://www.npmjs.com/package/vue-wordcloud 。
npm引入指令如下:cnpm install vue-wordcloud 。
WordCloud.vue 。
<template>
<el-card class="word-cloud-card">
<wordcloud
class="word-cloud"
:data="words"
nameKey="name"
valueKey="value"
:wordPadding="2"
:fontSize="[10,50]"
:showTooltip="true"
:wordClick="wordClickHandler"
/>
</el-card>
</template>
<script>
import wordcloud from "vue-wordcloud";
import apiService from "@/config/apiService.js";
export default {
name: "app",
components: {
wordcloud,
},
methods: {
wordClickHandler(name, value, vm) {
console.log("wordClickHandler", name, value, vm);
},
},
data() {
return {
words: [],
};
},
created() {
apiService
.get("/hotSearch/wordCloud/queryWordCloud?topN=100")
.then((res) => {
this.words = res.data.data.map((item) => ({
value: item.rate,
name: item.word,
}));
})
.catch((error) => {
// 处理错误情况
console.error(error);
});
},
};
</script>
<style scoped>
.word-cloud-card {
padding: 0% !important;
max-height: 300px;
margin-top: 10px;
}
.word-cloud {
max-height: 300px;
}
>>> .el-card__body {
padding: 0;
}
</style>
组件使用起来很容易,效果也还不错,但是造成了一个小BUG,用完这个组件后会导致小网站底部出现一个留白,现在都不知道怎么解决.
由于小网站的组件越来越多,整体的布局也需要重新设计一下,目前大概的布局如下:
布局使用的也是ElementUI自带的布局组件
<el-container>
<el-header> ... </el-header>
<el-main> ... </el-main>
<el-footer> ... </<el-footer>
</el-container>
搜索组件使用的是<el-autocomplete>,使用方法看API文档就可以了。组件不难,唯一要注意的是搜索出来的结果内容是可能会重复的,所以我们需要对结果加一个来源标识。 这里需要使用一个slot组装一个自定义组件,效果像这样:
组件代码如下:
<template slot-scope="{ item }">
<div style="display: flex; justify-content: space-between">
<span style="max-width: 280px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
{{ item.label }}
</span>
<span style="max-width: 80px; color: #8492a6; font-size: 13px; white-space: nowrap; " >
<img :src="getResourceInfo(item.resource).icon" style="width: 16px; height: 16px; vertical-align: middle"/>
{{ getResourceInfo(item.resource).title }}
</span>
</div>
</template>
具体的逻辑可以去看我的源码,我这里就不贴整个代码了.
这些小组件并不是一开始就想好要做的,大部分都是我突然灵机一动想起来才做的。可能有些东西看起来并不是那么有用,但是看着小网站的内容不断丰富起来感觉非常不错。这段时间我已经把全部的源码都提交到Gitee上了,但是还没来得及review,所以后面我除了分享怎么做组件外,还会跟大家分享我这4个月来遇到的一些BUG和问题,以及为什么我的代码要这样写.
头条的热搜接口返回的一串JSON格式数据,这就很简单了,省的我们去解析dom,访问链接是:[https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc) 。
ToutiaoHotSearchJob.java 。
package com.summo.sbmy.job.toutiao;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.summo.sbmy.common.model.dto.HotSearchDetailDTO;
import com.summo.sbmy.dao.entity.SbmyHotSearchDO;
import com.summo.sbmy.service.SbmyHotSearchService;
import com.summo.sbmy.service.convert.HotSearchConvert;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.collections4.CollectionUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static com.summo.sbmy.cache.hotSearch.HotSearchCacheManager.CACHE_MAP;
import static com.summo.sbmy.common.enums.HotSearchEnum.TOUTIAO;
/**
* @author summo
* @version ToutiaoHotSearchJob.java, 1.0.0
* @description 头条热搜Java爬虫代码
* @date 2024年08月09
*/
@Component
@Slf4j
public class ToutiaoHotSearchJob {
@Autowired
private SbmyHotSearchService sbmyHotSearchService;
@XxlJob("toutiaoHotSearchJob")
public ReturnT<String> hotSearch(String param) throws IOException {
log.info(" 头条热搜爬虫任务开始");
try {
//查询今日头条热搜数据
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder().url(
"https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc").method("GET", null).build();
Response response = client.newCall(request).execute();
JSONObject jsonObject = JSONObject.parseObject(response.body().string());
JSONArray array = jsonObject.getJSONArray("data");
List<SbmyHotSearchDO> sbmyHotSearchDOList = Lists.newArrayList();
for (int i = 0, len = array.size(); i < len; i++) {
//获取知乎热搜信息
JSONObject object = (JSONObject)array.get(i);
//构建热搜信息榜
SbmyHotSearchDO sbmyHotSearchDO = SbmyHotSearchDO.builder().hotSearchResource(
TOUTIAO.getCode()).build();
//设置知乎三方ID
sbmyHotSearchDO.setHotSearchId(object.getString("ClusterIdStr"));
//设置文章连接
sbmyHotSearchDO.setHotSearchUrl(object.getString("Url"));
//设置文章标题
sbmyHotSearchDO.setHotSearchTitle(object.getString("Title"));
//设置热搜热度
sbmyHotSearchDO.setHotSearchHeat(object.getString("HotValue"));
//按顺序排名
sbmyHotSearchDO.setHotSearchOrder(i + 1);
sbmyHotSearchDOList.add(sbmyHotSearchDO);
}
if (CollectionUtils.isEmpty(sbmyHotSearchDOList)) {
return ReturnT.SUCCESS;
}
//数据加到缓存中
CACHE_MAP.put(TOUTIAO.getCode(), HotSearchDetailDTO.builder()
//热搜数据
.hotSearchDTOList(sbmyHotSearchDOList.stream().map(HotSearchConvert::toDTOWhenQuery).collect(Collectors.toList()))
//更新时间
.updateTime(Calendar.getInstance().getTime()).build());
//数据持久化
sbmyHotSearchService.saveCache2DB(sbmyHotSearchDOList);
log.info(" 头条热搜爬虫任务结束");
} catch (IOException e) {
log.error("获取头条数据异常", e);
}
return ReturnT.SUCCESS;
}
@PostConstruct
public void init() {
// 启动运行爬虫一次
try {
hotSearch(null);
} catch (IOException e) {
log.error("启动爬虫脚本失败",e);
}
}
}
最后此篇关于《花100块做个摸鱼小网站!》第八篇—增加词云组件和搜索组件的文章就讲到这里了,如果你想了解更多关于《花100块做个摸鱼小网站!》第八篇—增加词云组件和搜索组件的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我需要从 1024 增加 FD_SETSIZE 值至 4096 .我知道最好使用 poll()/epoll()但我想了解什么是优点/缺点。主要问题是:我要重新编译glibc吗? ?我读了几个线程,其中
已关闭。这个问题是 not reproducible or was caused by typos 。目前不接受答案。 这个问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-top
我在 HTML 文件中有这样的内容: var value = 0; add(x){ x++; do
有没有办法在用户向上滚动时增加变量,并在用户使用 JavaScript 向下滚动时减少变量?变量没有最大值或最小值,如果能够调整灵敏度就好了。我不知道从哪里开始,感谢您的帮助! 编辑:没有滚动条,因为
我是 ios 新手,遇到以下问题。 我想根据表格 View 中元素的数量增加和减少表格 View 的高度大小。如果在输入时客户端在输出时给出 3 个或超过 3 个元素,我希望看到一个比默认行大 2 行
所以我一直在四处搜索,似乎大多数人认为以下列方式递增 indexPath 是正确的方法: NSIndexPath *newIndexPath = [NSIndexPath indexPathForRo
我有一个关于 connSupervisionTimeout 的问题。 我正在使用 CoreBluetooth 编写应用程序。我检查了连接参数和 connSupervisionTimeout = 720
我正在尝试根据页面的滚动位置更改元素的填充;当用户向下滚动页面时,填充会增加,而当他们向上滚动时,填充会减少。 我的主要问题是滚动不是很流畅,有时如果我滚动到页面顶部太快,每次元素的填充大小都不一样。
我正在尝试计算 18456 个基因的相关性度量,但编译器 (Dev C) 在将宏 GENE 或 INDEX 增加到 4000 到 5000 之间的值后退出或大。例如,它适用于: # define GE
我有一个带有 position: absolute 和 CSS3 过渡的圆形元素(a 元素)。在 hover 事件中,我想增加圆的高度和宽度,但我想在所有边上添加像素,而不仅仅是在左侧或右侧。 示例如
为了改善用户体验,我计划在我网站的所有页面(A-、A、A+)上增加/减少/重置字体大小 我面临的问题是页面上不同元素使用的字体大小不统一。有些是 14px,有些是 18px,有些是 12px,有些是
本文实例讲述了Yii框架数据库查询、增加、删除操作。分享给大家供大家参考,具体如下: Yii 数据库查询 模型代码: ?
sql替换语句,用该命令可以整批替换某字段的内容,也可以批量在原字段内容上加上或去掉字符。 命令总解:update 表的名称 set 此表要替换的字段名=REPLACE(此表要替换的字段名, '原
sql不常用函数总结以及事务,增加,删除触发器 distinct 删除重复行 declare @x 申明一个变量 convert(varchar(20),t
要增加我使用的最大可用内存: export SPARK_MEM=1 g 或者我可以使用 val conf = new SparkConf() .setMaster("loca
我正在尝试将文本(自定义文本按钮)放入 AppBar 的前导属性中。但是,当文本太长时,文本会变成多行 Scaffold( appBar: AppBar( centerTi
我正在使用最新版本的 NetBeans,我需要增加输出和菜单的字体大小(不是代码部分)。我试过: netbeans_default_options=".... --fontsize 16" 但是当我将
我必须将 180000 个点绘制到一个 EPS 文件中。 使用标准 gnuplot 输出尺寸点彼此太接近,这使得它们无法区分。有没有办法增加图像的宽度和高度? 最佳答案 是的。 set termina
我有一个带有输入字段的 twitter bootstrap 3 导航栏。我想增加输入字段的宽度。我已尝试设置 col 大小,但它不起作用。 html比较长,请引用bootply http://www.
我正在尝试增加 ggplot 标题中下划线的大小/宽度/厚度。我曾尝试使用大小、宽度和长度,但没有成功。 这是我所做的一个例子。 test <- tibble(x = 1:5, y = 1, z =
我是一名优秀的程序员,十分优秀!