导读:本期聚焦于深圳程序员创作的《Spring Boot 如何整合新闻 API 实现热点资讯聚合与定时推送?》,敬请观看详情。新闻类应用和内容平台大多离不开第三方资讯接口,如何把这些数据稳定地拉取下来、清洗后入库,再通过定时任务推送给用户,是后端开发里很典型的一套流程。本文以 Spring Boot 为核心,讲解如何调用新闻 API 获取热点资讯,利用 RestTemplate 或 WebClient 完成接口请求与解析,把不同来源的数据统一聚合到数据库中,并结合 Spring Task 实现定时抓取与消息推送。文中还会给出统一的返回结构设计、异常重试策略以及接口限流等实践经验,帮助你搭建一个稳定可扩展的资讯聚合服务。

内容聚合类系统在如今的开发需求中非常常见,无论是企业内部的资讯门户,还是面向用户的资讯类小程序,都绕不开从第三方新闻 API 拉取数据这一环节。很多人第一步就能把接口调通,但真正要做成稳定的服务,还需要处理数据格式统一、定时抓取、失败重试、消息推送等一系列问题。本文将以 Spring Boot 为基础,完整演示一套热点资讯聚合与推送方案的落地过程。

Spring Boot 如何整合新闻 API 实现热点资讯聚合与定时推送?

一、整体架构设计与依赖准备

在动手写代码之前,先明确整体的数据流向。一个典型的资讯聚合服务包含四个部分:抓取层负责调用外部新闻 API;解析层把不同来源的数据映射成统一的实体;存储层将数据落入数据库并做好去重;推送层通过定时任务把热点内容分发给用户。这种分层设计的好处是,后续新增一个新闻源时,只需要在抓取层和解析层扩展,不影响其他模块。

依赖方面,除了 spring-boot-starter-web 之外,建议引入 WebClient 所需的 spring-boot-starter-webflux、数据库相关的 mybatis-plus 或 spring-data-jpa,以及 lombok 简化实体类编写。下面是一个基础的 pom 依赖配置:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

这里同时引入了 web 和 webflux 两个 starter,Web 容器仍然使用 Tomcat,而 WebClient 可以作为 HTTP 客户端使用,两者并不冲突。相比传统的 RestTemplate,WebFlux 提供的 WebClient 支持异步非阻塞调用,在并发抓取多个新闻源时性能优势明显。

二、调用新闻 API 并统一数据模型

不同的新闻 API 返回的字段差异很大,比如有的接口返回 title、有的叫 news_title,日期格式也可能是时间戳或者字符串。为了方便后续处理,第一件事是设计一个统一的实体类,把所有外部数据都转换成这个结构。常见的聚合数据、天行数据等新闻接口,核心字段无非是标题、来源、链接、发布时间和分类,可以抽象成如下实体:

@Data
@TableName("t_news")
public class NewsArticle {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String title;        // 新闻标题
    private String source;       // 来源,如新浪、网易
    private String category;     // 分类,如科技、财经
    private String url;          // 原文链接
    private String digest;       // 摘要
    private LocalDateTime publishTime;
    private Date createTime;
}

接下来编写抓取服务。使用 WebClient 调用接口时,建议把 API 地址和密钥放到配置文件中,而不是硬编码在代码里,方便切换环境。抓取逻辑的核心是发起请求、解析 JSON、转换为实体列表这三步:

@Service
public class NewsFetchService {

    private final WebClient webClient;

    @Value("${news.api.url}")
    private String apiUrl;

    @Value("${news.api.key}")
    private String apiKey;

    public NewsFetchService(WebClient.Builder builder) {
        this.webClient = builder.build();
    }

    public List<NewsArticle> fetchHotNews() {
        NewsResponse response = webClient.get()
                .uri(apiUrl + "?key={key}&num=20", apiKey)
                .retrieve()
                .bodyToMono(NewsResponse.class)
                .block(Duration.ofSeconds(10));
        if (response == null || response.getNewslist() == null) {
            return Collections.emptyList();
        }
        return response.getNewslist().stream()
                .map(this::convertToArticle)
                .collect(Collectors.toList());
    }

    private NewsArticle convertToArticle(NewsItem item) {
        NewsArticle article = new NewsArticle();
        article.setTitle(item.getTitle());
        article.setSource(item.getSource());
        article.setDigest(item.getDigest());
        article.setUrl(item.getUrl());
        article.setPublishTime(parseTime(item.getCtime()));
        article.setCreateTime(new Date());
        return article;
    }
}

解析时间字段时要格外小心,很多接口返回的是秒级时间戳字符串,直接用 LocalDateTime.parse 会抛异常。建议写一个健壮的解析方法,兼容时间戳和常见格式字符串。另外,block 操作虽然方便,但如果聚合的新闻源较多,可以考虑用 Flux 合并多个请求,通过 subscribeOn 指定调度器实现并行抓取。

三、数据去重与定时抓取任务

新闻接口的数据是持续更新的,定时抓取时必然遇到重复数据的问题。最简单有效的方案是给原文链接字段建立唯一索引,入库时用 INSERT IGNORE 或者在代码层先查询再插入。数据量大的场景下,推荐使用 Redis 的 Set 结构缓存已抓取的链接摘要,新数据先过 Redis 判断,命中的直接丢弃,减轻数据库压力。

@Service
@RequiredArgsConstructor
public class NewsStoreService {

    private final NewsArticleMapper newsMapper;
    private final StringRedisTemplate redisTemplate;

    public int saveIfAbsent(List<NewsArticle> articles) {
        String key = "news:fetched:urls";
        int saved = 0;
        for (NewsArticle article : articles) {
            Boolean isNew = redisTemplate.opsForSet()
                    .add(key, article.getUrl());
            if (Boolean.TRUE.equals(isNew)) {
                newsMapper.insert(article);
                saved++;
            }
        }
        return saved;
    }
}

定时任务可以用 Spring 自带的 @Scheduled 注解实现,不需要额外引入 Quartz。下面的例子每隔三十分钟抓取一次,并且错开高峰时段。需要注意的是,启动类上必须加上 @EnableScheduling,否则任务不会执行:

@Component
@RequiredArgsConstructor
public class NewsSyncTask {

    private final NewsFetchService fetchService;
    private final NewsStoreService storeService;
    private final NewsPushService pushService;

    @Scheduled(cron = "0 */30 * * * ?")
    public void syncHotNews() {
        try {
            List<NewsArticle> articles = fetchService.fetchHotNews();
            int count = storeService.saveIfAbsent(articles);
            if (count > 0) {
                pushService.pushLatest(articles);
            }
            log.info("新闻同步完成,新增 {} 条", count);
        } catch (Exception e) {
            log.error("新闻同步失败", e);
        }
    }
}

这里有一个容易被忽视的细节:如果部署了多个实例,@Scheduled 任务会在每台机器上都执行一遍,造成重复抓取。解决办法是引入分布式锁,比如用 Redisson 的 tryLock,或者使用 xxl-job 这类任务调度平台统一管理触发。

四、热点推送的实现方式与容错处理

推送环节的选型取决于业务场景。面向 App 用户可以用极光推送、个推等第三方服务;面向公众号或企业内部可以用 Server 酱、企业微信机器人 Webhook。以企业微信机器人为例,只需要向指定的 Webhook 地址发送 POST 请求即可,实现成本非常低:

 String.format("【%s】%s\n%s",
                        a.getSource(), a.getTitle(), a.getUrl()))
                .collect(Collectors.joining("\n\n"));

        Map<String, Object> body = new HashMap<>();
        body.put("msgtype", "text");
        body.put("text", Collections.singletonMap("content", content));

        webClient.post()
                .uri(webhookUrl)
                .bodyValue(body)
                .retrieve()
                .bodyToMono(String.class)
                .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)))
                .block();
    }
}

外部 API 调用不可能百分之百成功,容错策略必不可少。上面代码中使用了 retryWhen 配置指数退避重试,这是 WebClient 自带的能力,比手写循环重试优雅得多。除此之外,还应该设置合理的超时时间,避免外部接口拖垮自身线程;对抓取任务做兜底日志,失败时记录上下文便于排查。

最后补充一点,如果新闻源较多,抓取频率较高,可以在入口处增加一层简单的限流,用 Guava 的 RateLimiter 控制对外部 API 的请求速率,既保护了对方的接口配额,也降低了被封禁的风险。整体方案跑通之后,后续扩展推荐功能、热点排名、用户订阅分类等能力,都可以在这个基础架构上平滑叠加。

Spring Boot新闻API资讯聚合修改时间:2026-09-03 22:41:13

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49851.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。