☰
Java批量抓取百度新闻与今日头条:从工程搭建到数据入库实战
2026/10/7 5:46:41 网站建设 项目流程

简介:面向Java开发者的新闻爬虫示例,聚焦百度新闻与今日头条的站内抓取场景,支持按指定关键字批量采集新闻标题、正文、发布时间等字段,并统一写入数据库,适合数据收集、新闻聚合或爬虫入门练习。压缩包体积仅19KB,共20个文件,其中十六个Java源码文件按功能划分,构建了从网址管理、页面请求、内容解析到数据入库的完整链路,一个XML与一个properties文件负责依赖或参数配置,另附README说明与gitignore文件,便于快速导入本地工程。已有448人学习浏览,资源轻量但覆盖爬虫核心流程:网址队列收集、HTTP请求发起、正则或XPath定位、数据持久化,同时涉及robots协议遵守与访问频率限制等反爬应对思路。读者可基于此快速搭建新闻采集原型,并根据自身需求扩展多站点支持或分布式抓取,也可作为Java网络编程与数据采集入门的参考。

1. 用 Java 按关键字批量抓取百度新闻与今日头条:这份资源能解决什么

做新闻舆情监控、内容聚合或垂直信息库的人,最常卡在两个点上:一是目标站点的数据怎么稳定地拿下来,二是拿下来的数据往哪里放、怎么查。这份“百度新闻、今日头条爬虫”的 Java 项目,把这两件事都串成了一条完整链路——按关键字抓取新闻标题、来源、发布时间和链接,再写入数据库。它不是那种用 Python 脚本三五行抓完就丢给 Excel 的玩具,而是带着清晰的工程结构:Maven 管理依赖、Spring Boot 组织服务、MySQL 承接落库,抓取、解析、存储、去重都分模块,适合想用 Java 技术栈正经做数据收集的人。不管你是刚学完 Java 想练手完整爬虫流程,还是需要快速给分析团队攒一批新闻明细数据,都可以拿这份资源当起点。有一点先讲清楚:任何爬虫都必须控制频率、遵守目标站点的访问规则,这份资源适合个人学习与低频率数据整理,别拿它怼着别人的服务器跑。

2. 项目结构与选型:从 pom.xml 到程序入口,看看这套爬虫怎么组织

2.1 资源内的目录逻辑:先看这几个文件再动手

拿到压缩包,解压开后典型的一层 Maven 工程结构:外层是SJT-code,内部有pom.xml、src/main、.gitignore和README.md。初学者最容易犯的错是一上来就点开某个.java文件狂看,忽略了对整个工程影响最大的pom.xml。这个文件决定了依赖能不能拉下来、编译级别够不够、最终能不能启动。我一般会按下面顺序拆:

打开pom.xml,第一件事确认三样:<parent>或<packaging>类型、JDK 编译版本(maven.compiler.source/target)、核心依赖清单。对于这类爬虫项目,常见依赖包括spring-boot-starter-web(提供 Web 和任务调度能力)、org.jsoup:jsoup(解析 HTML)、org.apache.httpcomponents:httpclient(发 HTTP 请求)、mysql-connector-java(数据库驱动)、lombok(简化实体类)、fastjson或jackson(解析头条接口的 JSON 数据)。依赖版本之间最容易打架,比如 Spring Boot 2.x 和 mysql-connector-java 5.x 配合是没问题的,但换成 MySQL 8 驱动就必须确认 URL 里要不要拼serverTimezone。

再看src/main下的包结构。这类项目通常是按职责分包而不是按技术分层:crawler(爬虫抓取与解析)、service(业务编排)、dao或mapper(数据库操作)、config(配置类)。分包越清晰,你后面替换解析逻辑或切换数据源越省事。拿这份资源来说,你完全可以不改动 service 层,只替换百度或头条的解析器,让它去抓其他同类新闻站点。最后粗略扫一眼README.md,但这份项目的 README 可能写得很简,真正的运行说明反而要靠你从 pom.xml 和 application 配置里反推。

2.2 为什么是 Java 而不是 Python:选型理由与技术点

很多人觉得新闻爬虫就该用 Python,毕竟requests加xpath三行就能抓一个页面。但把这个逻辑放到工程里,差别就出来了。Java 生态的爬虫项目,抓取和存库可以住在一个进程里:Spring Boot 自带定时任务、事务管理和连接池,解析完一条新闻直接交给 MyBatis 批量插入,整个过程有类型约束、有异常链路、有日志埋点,上了生产环境也比脚本好维护。用 Python 做数据探索确实最短路径,但要接到企业现有的 Java 系统里,要么起独立服务,要么靠调度脚本拼装,反而多了一道工序。

这份项目的技术栈也踩在 Java 爬虫的主流路线上:HTTP 请求走HttpClient,HTML 解析走Jsoup,JSON 解析走Fastjson或Jackson,存储走 MySQL。整套选型有一个很实际的理由——百度新闻搜索结果是 HTML 页面,正文信息和链接都嵌在 DOM 里,适合用 Jsoup 的选择器语法,它和 jQuery 选择器几乎同名,写起来顺手;头条的搜索结果页是前后端分离,数据从接口返回 JSON,适合 Jackson 直接反序列化成对象。一个项目同时覆盖两种主流抓取方式,这对想复用代码的人来说是最有价值的地方。

2.3 从关键字到入库:主流程设计与关键等待时间

整个爬虫业务的主流程可以用一句话概括:读关键字 → 构造目标站点 URL → 发起请求 → 解析页面/接口 → 提取新闻字段 → 去重 → 写入数据库 → 等待一段时间再抓下一个关键字。下面这段伪代码是这类项目最常见的编排方式,你拿到的资源大概率也是这个结构,只是类名和接口名可能不同:

public void crawlByKeywords(List<String> keywords) { for (String keyword : keywords) { try { List<NewsItem> baiduList = baiduCrawler.fetchByKeyword(keyword); newsService.batchSave(baiduList); Thread.sleep(3000); // 每个关键字之间停 3 秒 List<NewsItem> toutiaoList = toutiaoCrawler.fetchByKeyword(keyword); newsService.batchSave(toutiaoList); Thread.sleep(3000); } catch (Exception e) { log.error("keyword [{}] crawl failed", keyword, e); } } }

逻辑不复杂,但里面有三个细节值得注意。第一,batchSave方法的幂等性很关键,同一批数据重复抓取时必须被数据库拦下来,而不是每次插入都报主键冲突。第二,Thread.sleep(3000)被称为“犯罪时间”也不夸张,固定 3 秒容易被目标站点识别成机器,更稳妥的做法是3000 + new Random().nextInt(2000)随机睡眠。第三,爬虫程序最忌讳一个关键字抛异常就中断整体任务,上面这段把异常捕获放在关键字循环内部,能保证单个关键词失败不影响后续批次。这个编排逻辑可以在你拿到手后直接沿用,两个爬虫模块各自维护自己的抓取逻辑,互不干扰。

3. 实现百度新闻抓取:关键字 URL 模板与入库,包含字段设计

3.1 构造百度新闻搜索 URL:编码是关键,别只拼一个字符串

百度新闻在 PC 端的搜索地址,常用的是https://www.baidu.com/s?tn=news&word=关键字,tn=news表示走新闻频道而不是通用网页搜索。构造 URL 时有个十有八九会犯的错:直接用中文关键字拼字符串。HTTP 请求里携带中文必须经过百分号编码,Java 里标准做法是用URLEncoder.encode,否则你发出去的请求要么 400,要么服务端收到乱码后返回空结果。下面这段是这类项目里最通用的写法:

String baseUrl = "https://www.baidu.com/s"; String encodedKeyword = URLEncoder.encode(keyword, StandardCharsets.UTF_8.name()); String url = baseUrl + "?tn=news&word=" + encodedKeyword;

注意URLEncoder.encode静态方法会声明抛UnsupportedEncodingException,在现代 JDK 里你直接传StandardCharsets.UTF_8.name()即可,也可以用java.net.URL的构造器配合查询参数类来做编码,但上面这种字符串拼接依然是爬虫项目里最常见、最透明的做法。参数说明:tn=news是新闻频道标志;word是搜索关键字;如果还想限制时间范围,可以追加&rtt=1表示按时间排序,或者追加上游资讯平台的筛选参数,但前提是你先拿浏览器打开这个 URL 验证一下它能返回你想要的页面结构。关键字里如果带空格或+,编码后会出现+或%20,百度搜索能正常处理,不必过度处理。

3.2 Jsoup 解析搜索结构:选择器怎么配、空值怎么兜底

请求拿到 HTML 之后,解析层就是整个项目的“主战场”。百度新闻搜索结果页,每条结果通常包裹在一个带固定 class 的容器块里,标题在h3下的a标签中,摘要和来源在相邻的段落里。不过我要先给个心理建设:百度页面的 class 命名不是稳定的,改版频繁,不要盲信网上贴出来的选择器,拿到手第一件事是打开浏览器、F12 找到真实结构再对号入座。下面是常见的解析骨架:

Document doc = Jsoup.connect(url) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .timeout(8000) .get(); Elements items = doc.select("div.result"); for (Element item : items) { Element titleLink = item.selectFirst("h3 a"); Element summaryEl = item.selectFirst("div.c-summary, span.c-color-text"); if (titleLink == null) { continue; // 标题解析不到,这条直接跳过,不要让它带脏数据入库 } String title = titleLink.text(); String jumpUrl = titleLink.absUrl("href"); String summary = summaryEl == null ? "" : summaryEl.text(); }

解析逻辑有三个容易踩的位置。第一,select返回空集合是常态,尤其是目标页面改版后;每次解析前做null判断或空集合判断,比在数据库里做数据清洗省事得多。第二,titleLink.absUrl("href")会把相对路径自动拼接成绝对地址,这个比手动拼要好,因为百度搜索结果的链接有时候是//www.baidu.com/link?url=xxx这种协议相对写法。第三,摘要节点可能在页面上有多个结构版本,用逗号分隔多个选择器让 Jsoup 从中匹配任意一个,是应对页面改版最省事的兜底。解析出来的标题、URL、摘要,再结合外部传入的关键字和当前系统时间,就是一个完整的NewsItem对象。

3.3 数据库建表与带头去重复:字段名设计的好坏直接决定后续多省心

新闻数据入库,建表语句不能拍脑袋。抓下来的一条新闻,至少要记录关键字、标题、来源、发布时间、摘要、原始链接这几项。下面这份建表 SQL 是这类新闻数据最常用的结构,我在多个项目里验证过:

CREATE TABLE news_data ( id BIGINT PRIMARY KEY AUTO_INCREMENT, keyword VARCHAR(128) NOT NULL, title VARCHAR(512) NOT NULL, source VARCHAR(256), publish_time DATETIME, summary VARCHAR(1000), url VARCHAR(1024) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_keyword_url (keyword, url) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

表结构里有几个设计点很关键。UNIQUE KEY uk_keyword_url (keyword, url),把“关键字 + URL”作为唯一索引,这是新闻爬虫的去重基建。为什么要带keyword?因为同一条新闻可能被“比特币”和“区块链”两个关键字同时搜到,如果不带关键字去重,第二次抓取会被数据库挡下来,你按关键字统计时就少了一条关联数据。带关键字去重后,细节上是允许同一条 URL 出现在不同关键字结果的,这符合业务直觉。URL 字段为什么定1024?新闻外链经常带一堆统计参数,几百个字符是常态。字符集选utf8mb4而不是utf8,是为了兼容新闻标题里可能出现的表情符号和生僻字。数据插入时,在 MyBatis 里直接使用INSERT IGNORE配合唯一索引,一行代码就把重复问题消化在数据库层面:

// mapper 接口方法对应 SQL:INSERT IGNORE INTO news_data (...) VALUES (...) int inserted = newsDataMapper.insertIgnoreBatch(itemList); log.info("本次实际新增 {} 条", inserted);

INSERT IGNORE返回的影响行数是“真正新增”的条数,不是尝试插入的总数,这个返回值可以用来判断当批数据的重复率。重复率太高时,先反思是不是请求间隔太短导致页面结构没变化,或者关键字列表重复了。数据入库后,按关键字分组统计、按时间范围查询都属于常规操作,但注意字段名别踩保留字的坑,具体我在第五章展开。

4. 今日头条抓取与反爬应对:接口返回、请求头伪装与抓取频率

4.1 头条的数据源在接口里,不在 HTML 里

今日头条的电脑端页面是典型的前后端分离应用,直接抓 HTML 的话,你会得到一大坨 Vue 初始化脚本和空荡荡的 DOM,正文内容一个都见不着。这类网站的正确打开方式,是找到它背后的搜索数据接口。头条的搜索接口返回结构通常是 JSON,包含新闻标题、来源、发布时间、内容摘要和跳转链接。requests库能做的,Java 的HttpClient一样能做,区别只是响应体解析方式不同。实际抓的时候,先别急着写解析代码,在浏览器里打开开发者工具,Network 面板里找到search/list或search相关的请求,看它的Preview标签页返回的 JSON 里有哪些字段——这一步能帮你省掉两个小时瞎摸索。

接口请求的 URL 构造方式和百度搜索类似,关键字参数需要 URL 编码后再拼进去,以头条搜索接口为例,你通常能拿到接近这样的地址:https://www.toutiao.com/api/search/list/?keyword=关键字,同时在请求头里带上页面的必要信息。注意,头条的接口当前一般会带动态签名参数,它不是固定的拼接字符串能直接触发的,所以这份资源如果只是给了基础请求框架,你需要在本地先观察接口实际请求头与参数格式。我的习惯是:把返回体先打印一份保存成 JSON 文件,对照真实数据再去设计实体类,而不是凭空定义字段。头条新闻列表里同一篇文章可能在不同位置重复出现,靠group_id或者item_id做去重,比靠标题去重可靠得多。

4.2 请求头伪装:UA、Referer、Accept-Language 三个要配齐

很多爬虫失败的原因不是 IP 被封,而是请求头暴露了脚本身份。发出去的请求,服务器会先看 User-Agent,如果是空值或Java/1.8这种默认 UA,直接拒绝的概率非常大。头条这类站点对 UA 比较敏感,更完整的做法是配一组常见的浏览器 UA 字符串,每次请求随机抽一个用。除了 UA,Referer 和 Accept-Language 也是容易被忽略的两个字段。不设 Referer 时,部分服务器会认为这是外部冷请求;Accept-Language缺失,某些国内站点返回的内容编码判断会出问题。

下面是用 Apache HttpClient 构造头条请求的常见模板:

CloseableHttpClient client = HttpClients.custom() .setDefaultRequestConfig(RequestConfig.custom() .setConnectTimeout(5000) .setSocketTimeout(8000) .build()) .build(); HttpGet get = new HttpGet(url); get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"); get.setHeader("Referer", "https://www.toutiao.com/"); get.setHeader("Accept", "application/json, text/plain, */*"); get.setHeader("Accept-Language", "zh-CN,zh;q=0.9"); get.setHeader("Cookie", "你的浏览器登录态 Cookie,可选");

参数说明:setConnectTimeout是建立 TCP 连接的超时时间,头条接口偶发慢响应,5 秒比较稳妥;setSocketTimeout是等待响应的超时时间,8 秒够用且不会让程序长时间挂死。Cookie 这里要特别说明:冷启动时没带登录态也能拿到部分公开搜索数据,但如果接口返回的 JSON 里出现验证页特征,说明当前需要登录态或触发了风控。这时候不要靠“多试几次”去撞,正确做法是降低整体抓取频率、将任务分散到多个时间段执行,或者评估是否改用官方开放能力。爬虫的价值在于整理公开数据,而不是对抗别人的防护机制。

4.3 频率控制与重试:这段“刹车代码”才是项目能不能长期跑的关键

头条接口对频率的容忍度比百度新闻低,这是实战里最直观的血泪经验。第一次跑这类项目,我习惯把每次请求的间隔压在 “上一次请求结束到下一次请求开始” 的 2 到 5 秒随机范围内。写请求代码时顺手把“正在抓哪个关键字、当前是第几页、已抓多少条”全部打进日志,这样一旦被封,你能立刻定位是哪一步触发了风控。下面这段重试逻辑是对付偶发 5xx 和网络抖动的常用写法:

int retryTimes = 0; boolean success = false; while (retryTimes < 3 && !success) { try { CloseableHttpResponse response = client.execute(get); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 200) { // 这里先读取 response.getEntity() 并做 JSON 解析 success = true; } else if (statusCode == 403) { log.warn("触发风控,等待 30 秒后重试"); Thread.sleep(30_000); } else { Thread.sleep(5_000L * (retryTimes + 1)); } } catch (IOException e) { log.warn("请求异常,重试中", e); Thread.sleep(3_000L * (retryTimes + 1)); } retryTimes++; }

这段代码最值得学习的地方是“失败时等待时间递增”的退避策略(指数退避的简化版)。第一次失败等 5 秒,第二次失败等 10 秒,第三次失败等 15 秒,给服务器留出恢复窗口,而不是死命重试加速封禁。403单独处理成等待 30 秒,是因为它通常意味着你的请求频率已经触发了风控,短时间重试毫无意义。真实生产里我还会加一个熔断开关:连续 5 次 403 就暂停整个任务并给负责人发告警,因为继续跑下去只会让 IP 被封得更久。头条这类站点,每天换着时间段跑比一口气全量跑完更安全,这属于爬虫工程师的“玄学”,但确实有效。

5. 避坑排查:启动失败、乱码、字段撞名与触发风控的五个修复记录

5.1 项目拿回来跑不起来:依赖源和 JDK 版本两个原因

现象:mvn spring-boot:run启动时报错,控制台红字提示无法解析依赖或编译失败。新手最容易在这地方卡住,转头就去联系资源提供方,其实超过一半的问题出在本地环境。

原因分两类。一是 Maven 默认从中央仓库下载依赖,国内网络环境经常拉一半超时,导致.m2目录里残留损坏的 jar;二是 pom 里声明的编译版本(比如 1.8)和你本地装的 JDK 版本不匹配。解决办法也很直接:打开~/.m2/settings.xml,配置阿里云镜像仓库,把mirrorOf设为central;然后跑java -version和mvn -v确认 JDK 版本。如果本地是 JDK 17,而 pom 要求 1.8,需要安装一个对应版本的 JDK,或者在 pom 的maven.compiler.source和target上改成兼容的版本。这类项目我建议先跑一遍mvn clean package -DskipTests,跳过测试只做编译,编译通过了再谈运行。等你自己把这些基础环境排查完,还是没法启动,那时候再考虑联系资源方也不迟。

5.2 中文乱码:MySQL 表和连接串两侧都要顾

现象:日志里打印的标题是中文,一切正常;落库之后再查,全是???或者乱码符号。这个坑几乎人人都踩。

原因有两个,且常常同时发生:数据库表本身字符集不是utf8mb4;JDBC 连接串里没带编码参数。只改表字符集,连接层还是按 latin1 写入,照样乱码。解决方法是双管齐下,建表时在表定义末尾加DEFAULT CHARSET=utf8mb4,已经建错的表可以用ALTER TABLE news_data CONVERT TO CHARACTER SET utf8mb4修复;同时检查application.yml里的连接串:

spring: datasource: url: jdbc:mysql://localhost:3306/news_db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai

这几个参数useUnicode=true和characterEncoding=utf8mb4是中文数据入库的保险栓,serverTimezone=Asia/Shanghai解决插入publish_time时的小时差问题。修改完连接串,重启服务,新写入的数据就正常了。已经乱掉的历史数据只能删除重抓,所以在正式启动前先抓一条数据验证中文链路。

5.3 建表或插入时 SQL 语法报错:字段名撞了 MySQL 保留字

现象:执行 SQL 时报错,大概位置在keyword、source或order附近的语法错误。这段报错对没经验的人来说非常劝退,看着是标准 SQL 但就是过不去。

原因:MySQL 有一些保留字不能直接用作表名字段名,而“关键字”“来源”这类词恰恰容易撞车。这里的keyword就是典型撞名。解决办法有两个:一是建表时给每个可能冲突的字段加反引号包裹,例如`keyword` VARCHAR(128) NOT NULL;二是更推荐,直接改字段名,把keyword改成search_keyword,把source改成source_name,从根上消除隐患。爬虫项目建表时你还会遇到content、describe这类潜在冲突词,统一加前缀或者建表时在 MySQL 客户端里先试跑一遍,比落到 Java 代码里再去查错快得多。

5.4 存进库里的 URL 全是百度的跳转中间页

现象:url字段存下来的链接长这样:https://www.baidu.com/link?url=xxxxx,点开能跳转到真实新闻页,但直接抓正文源站时,这个链接不可用。

原因:百度搜索结果页里的链接不是新闻源站的直接地址,而是经过百度跳转服务包装的。数据入库时如果直接取href,存下来的就是这个中间链接。解决方式是在解析层做一层“还原”。常见做法有两种:第一种,解析baidu.com/link?url=后面的参数,结合重定向逻辑拿到真实外链;第二种,用HttpClient发一个HEAD请求,跟随重定向落点拿最终地址。但注意,这会增加一次额外请求,频率上要更克制。如果你只用这份数据做标题摘要层面的统计,不打算抓正文,那存跳转链接也可以接受;但如果后续要做源站正文采集,务必在入库前还原成真实外链。

5.5 爬了百十条突然被限制:固定 UA 和零间隔是最大诱因

现象:前 100 条抓得很顺利,然后突然连续 403,或者页面返回里出现验证码特征,再往后数据量就停在原地不动了。

原因:请求特征太像程序。固定一个 UA、请求间隔恒定 3 秒、串行循环不间断跑,这些信号叠加起来,服务器判断你是脚本的概率非常高。解决办法是把请求参数“做散”:维护一个 5 到 10 个浏览器 UA 的列表,每次请求随机取一个;请求间隔改成2000 到 5000毫秒随机;如果确实急需抓完,可以配置多组代理,但代价是代理稳定性不可控。还有一个容易被忽略的点:每次请求的目标页面不要共用同一个HttpClient实例长连接,部分站点的反爬会检测到 HTTP 连接复用特征,定时重建客户端能有效规避。最简单直接的止损操作是:连续出现三次异常状态码就暂停本轮任务,人工确认页面结构是否变化,再决定要不要继续。

6. 增量抓取与数据验证:让爬虫从一次性脚本变成可靠的数据来源

6.1 增量去重:好数据库结构应该让重复抓取自动失效

爬虫项目一旦跑起来,最大的烦恼不是抓不到数据,而是重复数据越来越多。我在前面建的唯一索引uk_keyword_url已经解决了大部分重复问题,这里再补一个更精确的时间过滤:只抓“今天”的新闻。百度新闻搜索本身就支持按时间范围筛选,在 URL 上追加时间参数就能减少大量无效抓取;头条接口也在结果里返回publish_time,在入库前判断如果发布时间不在目标窗口内就跳过。这样做的直接好处是:你每天定时执行抓取,库里的数据始终是最近几天的新文章,不是越滚越大的膨胀库。增量逻辑不要写到 SQL 里和数据库较劲,放在解析层用时间戳过滤是最轻松的。

每天定时跑一遍,我用的是 Spring 自带的调度注解,配置如下:

@Component public class NewsCrawlScheduler { @Scheduled(cron = "0 30 8 * * ?") public void dailyCrawl() { List<String> keywords = Arrays.asList("比特币", "人工智能", "新能源"); crawlService.crawlByKeywords(keywords); } }

0 30 8 * * ?表示每天早上 8 点 30 分执行一次。这个时间点避开业务高峰期,对站点服务器也更友好。需要定时跑多个关键字就把列表维护在配置文件里,外面改配置不需要重新编译。

6.2 抓完以后怎么验证数据质量:三条 SQL 看清全局

爬虫跑完,不能看一眼日志就收工。我每次都会从三个角度抽查数据:总量、关键字分布、发布时间分布。下面三条 SQL 是我固定会执行的:

-- 1. 总量是否合理:太少了可能页面解析失败,太多了可能关键字重复 SELECT COUNT(*) FROM news_data; -- 2. 按关键字看分布:如果某个关键字结果异常少,单独检查它的抓取日志 SELECT search_keyword, COUNT(*) FROM news_data GROUP BY search_keyword; -- 3. 按发布日期看最近趋势:确认多数文章集中在最近两天 SELECT DATE(publish_time) AS d, COUNT(*) FROM news_data GROUP BY DATE(publish_time) ORDER BY d DESC LIMIT 14;

如果第一条返回的数字接近 0,先不要怀疑目标网站没数据,多半是解析选择器失效。此时用浏览器打开目标搜索地址,查看页面结构是否新增了data属性或改了 class 名。如果第三条的发布时间全是 Null,很可能接口返回的时间字段不是标准时间格式,需要在解析层额外做一次时间格式化转换再写入。

6.3 把这份爬虫资源用稳的最后一块拼图

写爬虫项目,最忌讳的是把代码写完了就当它跑得好。我的习惯是:每次新接入一个站点或改动一个解析规则,先只抓取一个关键字、限制只存储 10 条,人工检查这 10 条数据的标题是否完整、URL 是否能打开、发布时间是否正确。确认没有异常后,再放开到全量关键字。这套“小批量验证”的习惯救了我很多次——以前我有一次直接拿着跑全量,结果百度页面刚好改版,一个选择器写错,批量抓下来的 3000 条数据里 2800 条标题都是“百度安全验证”,整个数据库直接没法用。从那以后,我每次启动爬虫任务都强制先走一遍小批量验证,看到日志里标题正常、入库条数合理,再放开全部关键字。希望这套流程帮你也少踩一些坑;拿到这份 Java 新闻爬虫项目后,改好数据库连接和关键字列表,用这一套方法稳稳跑起来。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询