当前位置: 首页 > news >正文

Springboot2.7集成websocket及相关问题

1、集成websocket完整代码

导入maven依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

服务端代码

(1)注入bean
@Configuration
@EnableWebSocket
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
(2)服务端开放的服务及其业务逻辑
@Slf4j
@Component
@ServerEndpoint("/testcase/page/{clientId}")
//@Lazy
public class WsMessageService {//与某个客户端的连接会话private Session session;//存放每个客户端对应的WebSocket对象。private static Map<String, WsMessageService> webSocketsBeanMap = new ConcurrentHashMap<>();//每次连接都是一个新的会话对象,线程安全的String userId;//注入业务类。要注意setTestcaseService()的名称不能错,set后的字符串要和要注入的变量名一致。private static TestcaseService testcaseService;@Autowiredpublic void setTestcaseService(TestcaseService testcaseService) {WsMessageService.testcaseService= testcaseService;}//private TestcaseService testcaseService = SpringContextUtil.getBean(TestcaseService.class);@OnOpenpublic void onOpen(Session session, @PathParam(value = "clientId") String userId) {this.session = session;this.userId = userId;webSocketsBeanMap.put(userId, this);log.info("OnOpen连接成功,userId:{},当前在线人数:{}", userId, this.getOnLineCount());}@OnMessagepublic void onMessage(String message) throws IOException {Session session = webSocketsBeanMap.get(this.userId).session;if (session==null || !session.isOpen()) {return;}log.info("收到客户端的消息:" + message);//start业务Integer pageNum = null;Integer pageSize = null;String pid = null;String vid = null;if (session.getRequestParameterMap().get("pageNum")!= null) {pageNum = Integer.valueOf(session.getRequestParameterMap().get("pageNum").get(0));}if (session.getRequestParameterMap().get("pageNum")!= null) {pageSize = Integer.valueOf(session.getRequestParameterMap().get("pageSize").get(0));}if (session.getRequestParameterMap().get("pid")!= null) {pid = session.getRequestParameterMap().get("pid").get(0);}if (session.getRequestParameterMap().get("vid")!= null) {vid = session.getRequestParameterMap().get("vid").get(0);}PageInfo<TestcasePageVO> pageInfo = testcaseService.findTestcaseByMultiCondition(pageNum, pageSize, pid, vid);//end业务try {JSONObject object = new JSONObject();object.put("data", pageInfo);object.put("code", 200);object.put("message", "SUCCESS");this.session.getBasicRemote().sendText(String.valueOf(object.toString()));} catch (IOException e) {throw new RuntimeException(e);}}@OnClosepublic void onClose() throws IOException {log.info("会话关闭,关闭会话的用户Id为:{}", this.userId);webSocketsBeanMap.remove(this.userId);log.info("当前在线人数:{}", this.getOnLineCount());}@OnErrorpublic void onError(Session session, Throwable error) {log.error("连接错误:" + error.getMessage());error.printStackTrace();}private int getOnLineCount() {return webSocketsBeanMap.size();}
}

测试服务端的可用性。在浏览器的控制台输入以下代码可以测试。

function initWebSocket(wsUri) {var websocket = new WebSocket(wsUri);websocket.onopen = function(evt) {console.log('连接建立中... '+wsUri);};websocket.onclose = function(evt) {console.log('连接关闭中...', evt);};websocket.onmessage = function(evt) {console.log('收到来自服务端的消息:', evt.data);};websocket.onerror = function(evt) {console.log('发生错误...', evt);};return websocket;}var websocket = initWebSocket("ws://ip:port/testcase/page/123?pid=5922cc3d03f74012a7112c931c8497d7&vid=fc2d7a6049ff4c7bafc40bf659b4e903");var msg, i = 0;var loop = setInterval(function(){msg = "Hello " +(i++);if(websocket.readyState == WebSocket.OPEN) {websocket.send(msg);console.log('已发送消息:' +msg);} else{clearInterval(loop);console.log('连接已关闭');}}, 10000);

2、集成websocket另一种方法(用控制台测试时返回的数据会乱码)

代码找不到了。但那种方法有一个特征就是使用的注解比当前的方法少。

3、解决业务类注入不进去的问题(两种方法,推荐第一种)

(1)第一种:就是当前使用的注入方法。注意命名,否则不会注入成功

//注入业务类。要注意setTestcaseService()的名称不能错,set后的字符串要和要注入的变量名一致。private static TestcaseService testcaseService;@Autowiredpublic void setTestcaseService(TestcaseService testcaseService) {WsMessageService.testcaseService= testcaseService;}

(2)第二种:自己写一个SpringContextUtil工具类,协助注入

package cn.xxxxx.tmMaster.utils;import lombok.extern.slf4j.Slf4j;
import org.mybatis.logging.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import java.util.logging.Logger;@Slf4j
@Component
public class SpringContextUtil implements ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextUtil.applicationContext = applicationContext;}/*** 获取 applicationContext*/public static ApplicationContext getApplicationContext() {return applicationContext;}/*** 通过 name 获取 bean 对象*/public static Object getBean(String name) {return getApplicationContext().getBean(name);}/*** 通过 class 获取 bean 对象*/public static <T> T getBean(Class<T> clazz) {return getApplicationContext().getBean(clazz);}/*** 通过 name,clazz  获取指定的 bean 对象*/public static <T> T getBean(String name, Class<T> clazz) {return getApplicationContext().getBean(name, clazz);}
}

在WsMessageService中使用如下代码注入进去,但在类上必须要@Lazy进行懒加载,否则会报错

@Lazy
public class WsMessageService {
……private TestcaseService testcaseService = SpringContextUtil.getBean(TestcaseService.class);
……
}

不加@Lazy报错如下:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wsMessageService' defined in file [D:\javaworkspace\test-xxxx\tm-master\target\classes\cn\xxxxx\tmMaster\service\testcase\WsMessageService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.xxxxx.tmMaster.service.testcase.WsMessageService]: Constructor threw exception; nested exception is java.lang.NullPointerException

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wsMessageService' defined in file [D:\javaworkspace\test-xxxx\tm-master\target\classes\cn\xxxxx\tmMaster\service\testcase\WsMessageService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.xxxxx.tmMaster.service.testcase.WsMessageService]: Constructor threw exception; nested exception is java.lang.NullPointerExceptionat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1334) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921) ~[spring-context-5.3.29.jar:5.3.29]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.29.jar:5.3.29]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.14.jar:2.7.14]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) [spring-boot-2.7.14.jar:2.7.14]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.14.jar:2.7.14]at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.7.14.jar:2.7.14]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.7.14.jar:2.7.14]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.7.14.jar:2.7.14]at cn.xxxxx.tmMaster.MasterApplication.main(MasterApplication.java:17) [classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_382]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_382]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_382]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_382]at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) [spring-boot-devtools-2.7.14.jar:2.7.14]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.xxxxx.tmMaster.service.testcase.WsMessageService]: Constructor threw exception; nested exception is java.lang.NullPointerExceptionat org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:224) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87) ~[spring-beans-5.3.29.jar:5.3.29]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1326) ~[spring-beans-5.3.29.jar:5.3.29]... 22 common frames omitted
Caused by: java.lang.NullPointerException: nullat cn.xxxxx.tmMaster.utils.SpringContextUtil.getBean(SpringContextUtil.java:55) ~[classes/:na]at cn.xxxxx.tmMaster.service.testcase.WsMessageService.<init>(WsMessageService.java:48) ~[classes/:na]at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_382]at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_382]at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_382]at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_382]at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211) ~[spring-beans-5.3.29.jar:5.3.29]... 24 common frames omitted

参考资料:

SpringBoot集成WebSocket实现客户端与服务端长连接通信_springboot实现websocket客户端_拄杖忙学轻声码的博客-CSDN博客 SpringBoot集成WebSocket实现客户端与服务端长连接通信

快速搭建springboot websocket客户端_springboot实现websocket客户端_wcybaonier的博客-CSDN博客 快速搭建springboot websocket客户端

相关文章:

Springboot2.7集成websocket及相关问题

1、集成websocket完整代码 导入maven依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency> 服务端代码 &#xff08;1&#xff09;注入bean Configur…...

MES管理系统和ERP系统在生产制造管理中的应用

MES生产管理系统通过过程管理、质量管理、设备管理、产品跟踪和溯源、性能分析和物料管理等方面来管理生产制造&#xff0c;旨在建立规范的生产管理信息平台&#xff0c;提高企业核心竞争力。ERP系统则通过制定生产计划、细分物料需求计划、车间订单下达和生产回报等步骤进行生…...

Netty Channel 详解

优质博文&#xff1a;IT-BLOG-CN 一、Netty 服务端启动过程 【1】创建服务端Channel&#xff1b; 【2】初始化服务端Channel&#xff1b; 【3】注册Selector&#xff1b; 【4】端口绑定&#xff1a;我们分析源码的入口从端口绑定开始&#xff0c;ServerBootstrap的bind(int in…...

技师学院物联网实训室建建设方案

一、概述 1.1专业背景 物联网&#xff08;Internet of Things&#xff09;被称为继计算机、互联网之后世界信息产业第三次浪潮&#xff0c;它并非一个全新的技术领域&#xff0c;而是现代信息技术发展到一定阶段后出现的一种聚合性应用与技术提升&#xff0c;是随着传感网、通…...

SpringBoot项目--电脑商城【增加/减少购物车商品数量】

1.持久层[Mapper] 1.1规划需要执行的SQL语句 1.更新该商品的数量.此SQL语句无需重复开发 update t_cart set num?,modified_user?,modified_time? where cid? 2.首先进行查询需要操作的购物车数据信息【查看该条数据是否存在】 SELECT * FROM t_cart WHERE cid?2.接口…...

CSS元素浮动

概述 浮动简介 在最初&#xff0c;浮动是用来实现文字环绕图片效果的&#xff0c;现在浮动是主流的页面布局方式之一。 元素浮动后的特点 脱离文档流。不管浮动前是什么元素&#xff0c;浮动后&#xff0c;默认宽与高都是被内容撑开的&#xff08;尽可能小&#xff09;&am…...

MATLAB中islocalmin函数用法

目录 语法 说明 示例 向量中的局部最小值 矩阵行中的最小值 相隔最小值 最小值平台区 突出最小值 islocalmin函数的功能是计算局部最小值。 语法 TF islocalmin(A) TF islocalmin(A,dim) TF islocalmin(___,Name,Value) [TF,P] islocalmin(___) 说明 ​当在 A 的…...

Python+Requests+Pytest+YAML+Allure实现接口自动化

本项目实现接口自动化的技术选型&#xff1a;PythonRequestsPytestYAMLAllure &#xff0c;主要是针对之前开发的一个接口项目来进行学习&#xff0c;通过 PythonRequests 来发送和处理HTTP协议的请求接口&#xff0c;使用 Pytest 作为测试执行器&#xff0c;使用 YAML 来管理测…...

双视觉Transformer(Dual Vision Transformer)

摘要 已经提出了几种策略来减轻具有高分辨率输入的自注意机制的计算&#xff1a;比如将图像补丁上的全局自注意过程分解成区域和局部特征提取过程&#xff0c;每个过程都招致较小的计算复杂度。尽管效率良好&#xff0c;这些方法很少探索所有补丁之间的整体交互&#xff0c;因…...

MES系统成为工业4.0首选,制造业真正数字化车间你看过吗?

在日益激烈的市场竞争中&#xff0c;MES管理系统已经成为企业提升生产效率、降低成本、提高竞争力的关键。通过MES管理系统实现数据集成和分析&#xff0c;能够对产品制造过程的各个环节进行可视化控制&#xff0c;从设计、制造、质量、物流等环节全面掌控信息&#xff0c;实现…...

Vuex有几种属性以及它们的意义

有五种&#xff0c;分别是 State、 Getter、Mutation 、Action、 Module。 一、State Vuex 使用单一状态树——是的&#xff0c;用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着&#xff0c;每个应用将仅仅包含一个 store 实…...

PRBP20P-10/250C-EB、PRDP6G-10/30-CB电液比例直动式先导减压阀放大板

PRDP6P-10/30-CB、PRDP6R-10/50-DC、PRDP6G-10/30-CC、PRDP6P-10/50-CB、PRDP6R-10/30-CC、PRDP6G-10/30-CB电液比例直动式先导减压阀 PRBP10P-10/50C-EB、PRBP20P-10/100C-EC、PRBP30P-10/150C-EB、PRBP20P-10/250C-EB、PRBP10P-10/315C-EC、PRBP30P-10/350C-EB电液比例柱塞平…...

GDB之常见缩写命令(十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

MarkText快捷键(随时补充中)

MarkText快捷键 ctrl1&#xff1a;一号标题 &#xff08;需要手动在【左上角】-【file】-【preferences】-【Key Bindings】-【 Transform into Heading 1】手动调整&#xff0c;先将【Switch tab to the 1st】占用快捷键删除才能在下面添加&#xff09; ctrlg&#xff1a;添加…...

每日一题 1601最多可达成的换楼请求数目(子集模版)

题目 1601 我们有 n 栋楼&#xff0c;编号从 0 到 n - 1 。每栋楼有若干员工。由于现在是换楼的季节&#xff0c;部分员工想要换一栋楼居住。 给你一个数组 requests &#xff0c;其中 requests[i] [fromi, toi] &#xff0c;表示一个员工请求从编号为 fromi 的楼搬到编号为…...

排序算法-归并排序

属性 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&#…...

vue3 整合 springboot 打完整jar包

前端 .env.developmen VITE_APP_BASE_URL/api.env.production VITE_APP_BASE_URL/axios 配置 axios.defaults.baseURL import.meta.env.VITE_APP_BASE_URLpackage.json "scripts": {"dev": "vite --mode development","build": &…...

依赖倒转原则是什么?

依赖倒转原则&#xff08;Dependency Inversion Principle&#xff09;是面向对象设计中的另一个基本原则&#xff0c;它是由Robert C. Martin提出的&#xff0c;它的中心思想是面向接口编程&#xff0c;该原则指出高层模块不应该依赖于低层模块&#xff0c;两者都应该依赖于抽…...

什么是GPT与MBR

GPT&#xff08;GUID Partition Table&#xff09;和MBR&#xff08;Master Boot Record&#xff09;是两种不同的磁盘分区表格式。 MBR是一种较早的磁盘分区表格式&#xff0c;它使用512字节的扇区作为存储空间。MBR分区表可以定义最多4个主分区&#xff0c;每个主分区都可以…...

前后端开发接口联调对接参数

前言 一个完整的互联网系统项目,需要前后端配合,进行上线,针对前端开发者,现在互联网主流的项目都是前后端分离 也就是后端负责提供数据接口,前端负责UI界面数据渲染 凡是在前台数据展示与用户交互的,都是由前端来实现的,而数据来源是由后台服务提供的 在浏览器c端能够发送后端…...

windows用户有哪些必备的小工具软件能大幅提高效率而且占用资源低?

Windows用户&#xff0c;你是否也遇到过这些痛点&#xff1f;软件占用大、效率不高、甚至广告多&#xff1f;本文整理13款经过网友高口碑的工具&#xff0c;涵盖截图标注、文件管理、系统优化等场景&#xff0c;全部免费无广告且占用资源极低&#xff0c;助你打造丝滑流畅的办公…...

强烈安利! AI论文工具,千笔AI VS 灵感风暴AI,专科生必备神器!

随着人工智能技术的迅猛发展&#xff0c;AI辅助写作工具已逐渐成为高校学生完成论文的重要帮手。无论是开题报告、文献综述还是整篇论文的撰写&#xff0c;越来越多的学生开始借助AI工具提升效率、降低写作难度。然而&#xff0c;面对市场上种类繁多的AI写作软件&#xff0c;许…...

书匠策AI:期刊论文写作的“魔法棒”大揭秘

在学术的神秘花园里&#xff0c;期刊论文宛如娇艳的花朵&#xff0c;是研究者智慧与心血的结晶&#xff0c;也是学术交流的重要信物。然而&#xff0c;撰写一篇出色的期刊论文&#xff0c;就像精心雕琢一件艺术品&#xff0c;需要耗费大量的时间和精力&#xff0c;还常常会遇到…...

Harmonyos应用实例91:3D视角切换器(观察物体)

应用实例一:3D视角切换器(观察物体) 知识点:能辨认从不同方向(前面、上面、左面)观察到的几何体的形状。 功能:学生可以通过手指滑动旋转一个由小正方体搭建的3D模型,并点击按钮切换到特定视角(前面、上面、左面),应用会高亮显示该视角下的平面图形。这能有效培养学…...

Audio Pixel Studio入门必看:晓晓、云希等高保真音色调用与语速参数详解

Audio Pixel Studio入门必看&#xff1a;晓晓、云希等高保真音色调用与语速参数详解 1. 快速了解Audio Pixel Studio Audio Pixel Studio是一款基于Streamlit开发的轻量级音频处理Web应用&#xff0c;专为需要高效语音合成和基础音频处理的用户设计。它最大的特点是集成了Mic…...

探索DAIR-V2X:构建车路协同自动驾驶的开源生态系统

探索DAIR-V2X&#xff1a;构建车路协同自动驾驶的开源生态系统 【免费下载链接】DAIR-V2X 项目地址: https://gitcode.com/gh_mirrors/da/DAIR-V2X 在智能交通技术快速演进的今天&#xff0c;DAIR-V2X作为领先的车路协同自动驾驶开源框架&#xff0c;正通过融合多模态感…...

3步根治开源工具性能瓶颈,核心指标提升200%的技术优化指南

3步根治开源工具性能瓶颈&#xff0c;核心指标提升200%的技术优化指南 【免费下载链接】go-cursor-help 解决Cursor在免费订阅期间出现以下提示的问题: Youve reached your trial request limit. / Too many free trial accounts used on this machine. Please upgrade to pro.…...

零基础玩转Granite-4.0-H-350M:Ollama快速部署,支持12种语言对话

零基础玩转Granite-4.0-H-350M&#xff1a;Ollama快速部署&#xff0c;支持12种语言对话 你是不是也想在本地电脑上跑一个AI助手&#xff0c;但又担心配置复杂、电脑带不动&#xff1f;今天要介绍的Granite-4.0-H-350M&#xff0c;可能就是你在找的那个“刚刚好”的模型。 它…...

阿里CosyVoice语音克隆实测:3秒音频克隆你的声音,效果惊艳

阿里CosyVoice语音克隆实测&#xff1a;3秒音频克隆你的声音&#xff0c;效果惊艳 最近在帮一个做有声书的朋友找语音合成方案&#xff0c;他需要为不同角色快速生成不同音色的旁白。试过几个在线平台&#xff0c;要么音色库固定不够用&#xff0c;要么定制声音的价格高得离谱…...

快速验证抓取方案:快马AI助你十分钟搭建openclaw部署原型

最近在做一个社交媒体信息抓取的小项目&#xff0c;需要快速验证一下技术方案的可行性。核心需求是能抓取微博或知乎这类平台上的公开话题信息&#xff0c;比如标题、正文和发布时间。如果从零开始搭建&#xff0c;光是配置环境、写爬虫规则、处理反爬、设计存储和调度&#xf…...