1. 先别急着写代码:把"获取新闻"拆成四个问题
做新闻内容获取这件事,很多人第一反应是"写个爬虫把几个网站的数据扒下来",然后往数据库一丢。我在接触这个需求的时候也是这条路,但实际做下来发现,获取新闻这个动作本身并不难,难的是在动手之前把下面几个问题想清楚:内容给谁看、要什么深度、能接受多大延迟、以及打算存多久。新闻内容获取和普通网页抓取最大的差异在于,它的数据是动态的、强时效的、且高度冗余的。你抓一百个站点,可能其中六十个在报道同一件事,剩下四十个里又有三十个是旧闻或者营销软文。如果没有提前设计好过滤和归档策略,最后得到的不是信息库,而是一堆电子垃圾。
1.1 人的阅读场景与机器的数据处理场景,走的是两条路
新闻内容获取的第一步,是区分你到底在做"给人看的聚合阅读"还是"给机器用的数据集"。这两个场景对数据的要求完全不同。
聚合阅读场景下,你关心的是标题、正文、发布时间、来源站点,偶尔需要封面图。用户体验的优先级远高于数据完整性。比如做一个每日简报,用户只想看到"今天发生了什么",至于这篇稿子中间改了几版、转载路径如何,根本不需要关心。这个场景下获取新闻的关键词是"精炼",抓到的内容需要去重、过滤、排序,最后以简洁结构呈现。
数据处理场景则反过来。比如你想做舆情分析、热点趋势预测,或者训练一个文本分类模型,这时候新闻内容获取的核心是"全量"。你需要保留发布时间、采集时间、原始HTML结构、多版本文档快照,甚至是阅读量、评论数这类交互数据。因为后续做分析时,你没法预判哪些字段有用,宁可多存不能漏存。
我见过一个真实的案例,某开发者做了一个热点聚合面板,最初按照阅读场景设计,只存了标题和链接。后来想在面板上叠加"热度趋势曲线",才发现历史数据没有保留,从那天起往回补数据几乎不可能,因为新闻网站会把旧页面归档甚至删除。所以动手之前,先问自己一个问题:这份数据未来会不会有二次加工的可能性?如果答案是"可能",那存储的粒度就要更细。
1.2 把需求参数化,避免拿到一堆用不上的数据
除了用途,还需要把需求转化为可量化参数。这是很多教程不会强调但实际干活时绕不开的一步。我习惯用一张表把需求固下来:
| 参数维度 | 取值示例 | 影响的技术决策 |
|---|---|---|
| 来源站点数量 | 1个垂直站点 vs 50个综合站点 | 决定是单源深度采集还是多源调度 |
| 更新频率 | 实时推送 vs 每30分钟轮询 vs 每日一次 | 决定用长连接推送还是定时任务 |
| 内容深度 | 只要标题 vs 标题+正文 vs 需要评论互动数据 | 决定解析深度和存储结构 |
| 输出格式 | 纯文本 / Markdown / JSON / RSS | 决定管道后端的序列化方案 |
| 保留周期 | 24小时滚动 / 永久归档 | 决定是否需要冷热分层存储 |
把这些问题写在纸上之后,"获取新闻"就从一个模糊的口号变成了具体的工程量估算。比如你要抓50个站点、每30分钟轮询一次、只存标题和链接,那这个项目大概率一天就能跑起来;如果你要抓100个站点、保留全文和阅读量、还要做实时推送,那就是一个需要消息队列、任务调度和全文提取模块的中型系统,周期至少以周计。
如果原始项目正文什么都没给,我的建议是从最小可行版本起步,先抓一两个高质量来源,跑通全链路,再横向扩展来源数量。这比一开始就追求"全网新闻"要可持续得多,也更容易排查管道里的问题。
2. RSS和公开API是低投入高回报的入口,但不是万能解药
在聊自建抓取链路之前,必须先把RSS和公开API这两个相对轻量的方案说清楚。因为这俩方案在很多场景下可以替代采集,帮你节省大量开发和维护成本。
2.1 RSS的价值被严重低估了:先探测再决定要不要爬
很多人以为RSS已经是老古董,实际上大量新闻站点至今仍在维护RSS输出,只是入口藏得比较深,没有主动宣传而已。判断一个站点有没有RSS,通常只需要尝试几个固定路径:/rss、/feed、/atom.xml、/rss.xml,或者在页面源码里搜索application/rss+xml这个link标签。如果目标站点在后台系统上做过标准化,RSS的可用性往往比网页解析稳定得多——它本身就是机器可读的结构化格式,发布时间、标题、链接都是现成的,连正文提取都省了。
在实际项目中,我搭建的第一版新闻管道就是基于纯RSS的。用Python的feedparser库解析订阅源,写成一个半小时轮询的定时任务,命中新条目之后推送到内部接口。整套东西加起来不到两百行代码,运行了几个月没有出过严重的解析故障。后来要扩展新闻源时,发现有的站点RSS只输出标题和一段摘要,正文需要跳回原文页抓取,这时候才引入自建采集模块。
这里有一个经验技巧:RSS里的updated字段和published字段经常不一致,有的站点订阅源输出的是编辑修改时间。做增量更新时,不能只看published大于上次游标就认为这是新文章,还应该结合条目唯一ID(通常是guid或id字段)做一个历史集合,防止重复入库。
2.2 公开API的情况:认证、限流与字段不全
有些新闻平台和内容聚合服务会提供公开API,看起来比RSS更"正规",但实际用起来限制不少。多数API要求注册申请一个密钥,有每日请求上限,返回的字段也受平台策略影响。比如有的接口可以拿到全文,有的只返回元数据;有的API对历史数据有范围限制,你只能获取近30天的内容。这些限制在设计数据管道之前就要排摸清楚,否则很容易做到一半发现数据源能力跟不上需求。
我的习惯是,在项目初期把RSS和公开API都列进一份调研清单,逐个验证以下四项:
- 是否提供全文?还是只有摘要?
- 是否允许商业化使用?条款里有没有明确禁止转储?
- 请求频率上限是多少?是否支持分页拉取历史数据?
- 字段里有没有唯一标识符和准确发布时间?
如果这四项里有任何一项不满足核心需求,我才会把这个来源标记为"需要自建采集方案"。注意,是"不满足核心需求"才转采集,而不是"这个源看起来没API所以直接爬"。RSS本身也是一种合法的结构化入口,不应跳过。
3. 自建抓取链路:请求频率、正文提取和增量更新才是工程重点
当RSS和API覆盖不了需求时,自建采集链路就是新闻内容获取的核心重头戏了。这一段我会按"请求层→解析层→存储层"的顺序来拆,每一步给出可复用的判断准则。
3.1 先说请求层:频率控制和robots协议是底线
自建采集最怕的不是技术难度,而是对目标站点造成压力。新闻网站的服务器通常没有很强的反爬能力,但也不能因此就高并发去怼。我在项目中的请求策略是:单站点请求间隔不小于3秒,并发连接数不超过2,并且全程携带真实可联系的User-Agent。这个频率对绝大多数中小站点来说是友好的。
有没有更强的策略?有。比如对单个站点做随机的5~15秒延迟区间抖动,或者在目标站点声明了Crawl-delay参数时严格遵循。这些做法不是为了破解反爬,而是为了降低自身请求的"特征密度",让采集行为更像一两个普通读者在浏览页面,而不是一台无感情的刷量机器。
robots协议方面,我个人的原则是:**个人项目应该遵守,商业项目必须遵守。**robots.txt不是法律,但它代表站点运营方对自动访问的明确态度。新闻站点的robots里经常会出现Disallow: /tag/、Disallow: /search/这类规则,说明运营方不希望自动程序去抓标签页和搜索结果页。即便这些页面里可能包含有价值的关键词聚合内容,也不要为了数据完整性去突破这个边界,这是这类项目能否长期持续的前提。
3.2 正文提取:从正则到通用提取库
拿到HTML之后,最核心的工作是提取标题、发布时间、正文、作者这四个字段。新手常见的错误是一上来就写正则。正则爬虫确实能应对单个站点,但一旦来源扩展到二三十个,每个站点的HTML结构都不同,正则维护量会呈指数上升。更靠谱的做法是分层解析:
第一层,优先尝试解析页面里的结构化元数据。新闻页面一般会嵌入JSON-LD或Microdata格式的元信息,里面字段非常规整。用解析库抽取headline、datePublished、author、articleBody这些键值对,效果远好于肉眼去看HTML结构。做法是在HTML源码中定位<script type="application/ld+json">标签,然后直接用JSON解析即可。
第二层,如果没有结构化元数据,使用通用正文提取库。现在流行的是基于文本密度和标签排版的算法,这类库可以自动估算正文区域,我的实测经验是准确率可以达到八成以上,尤其适合没有复杂交互逻辑的传统新闻站。
第三层,兜底方案才是针对特定站点的定制规则。把所有定制规则集中放在一个配置文件里,按域名路由,而不是散落在代码各处。
时间字段的提取尤其值得注意。新闻页面里的时间表达花样百出:"2024-05-12 09:30"、"12日9时30分"、"5小时前"、"刚刚"。自动化处理时,我一般写一个标准化的解析函数:先用正则匹配绝对时间格式;匹配不到再处理相对时间,比如"5小时前"用当前时间减去5小时;最后兜底用页面URL里可能存在的日期路径(如/2024/05/12/)作为发布时间。这个兜底链虽然不完美,但在生产环境里的覆盖率能到95%以上。
3.3 增量更新与存储:别做无脑全量重抓
采集管道跑起来之后,下一个坑是数据更新策略。最蠢的方案是每次轮询都把所有文章重新抓一遍,不仅白白消耗源头带宽,还会产生大量重复数据。新闻内容获取的工程里,增量更新是标配。
增量更新的核心是一个"游标"加一个"去重集合"。游标记录每个来源站点最近一次成功采集到的文章时间,下一次轮询只拉取这个时间之后的新内容。去重集合则记录最近N天已见到的文章ID集合,用于防范游标回退和站点端时间字段错乱导致的重复入库。
存储层的话,我建议不要一上来就上重型数据库。起步阶段SQLite就能扛得很稳,单表存标题、正文、来源、链接、发布时间、采集时间、去重指纹这几个字段就够了。等到数据量超过百万条再考虑迁移到PostgreSQL或者更专业的存储方案。正文过长的字段建议单独拆表,避免主表膨胀拖慢查询。
4. 去重、时效分层与噪声过滤:获取新闻的临门一脚
数据抓到本地之后,真正的信息处理才刚刚开始。新闻领域的数据噪声极高,不去重不过滤直接把数据推给用户,体验会非常差。这一节讲的都是我在实操中总结出来的过滤策略。
4.1 URL规范化和标题指纹,双击打掉转载文
新闻转载极其普遍。同一个事件,A站发一篇原创,B站转载,C站洗个稿,三篇内容在数据管道里会同时入库。去重是新闻内容获取里绕不开的环节。我的去重分两个层面:
第一层是URL规范化。很多站点会给外链拼接跟踪参数,比如?utm_source=xxx&from=xxx,这些参数对内容没有语义价值,只影响链接唯一性。在入库前统一做URL清洗,把无用的query参数摘除,就能直接消除大量"看起来不同其实是同一篇文章"的重复记录。这个操作成本极低,收益却非常直接。
第二层是标题指纹。清洗标题里的全角/半角空格、大小写、标点符号,再计算一个短hash就成了标题指纹。但只靠精确hash无法处理改写后的标题,比如把"某公司发布新一代产品"改成"某公司重磅发布新一代旗舰产品"。所以我在精确hash之外再加一层相似度判断:把标题按2-4个字符切分成ngram集合,计算Jaccard相似度,超过0.85就判定为同题文章。这个方法朴素,但在中文新闻标题里去重效果出乎意料地好。需要注意的是,为了防止误杀,这个相似度判断应该只在相近时间窗口内执行,比如24小时之内,跨天的旧文章不做这种高成本计算。
4.2 时效分层:不是所有新闻都一样"新鲜"
新闻内容获取的场景里,"新鲜"和"重要"是两个不同维度的指标。服务器宕机这种突发事件,时效性极强,几小时内就失去推送价值;而深度报道、行业综述这类内容,即便过了三天依然有阅读价值。所以不能用一个统一的定时器去管理所有内容。
我的做法是在入库时给每条新闻打一个"时效衰减系数",根据文章类型分为三档:
| 时效档位 | 典型内容 | 推送优先级 | 有效窗口 |
|---|---|---|---|
| 突发档 | 事故、快讯、实时行情 | 立即推送 | 2-6小时 |
| 常规档 | 普通新闻、公告、产品发布 | 批次推送 | 24-48小时 |
| 长效档 | 深度分析、访谈、专题 | 不做实时推送 | 3-7天 |
分类的判断很简单,看标题和正文里有没有时间敏感词,或者看URL路径结构里有没有日期。这套分层的好处是,它让推送模块知道哪些内容值得占用用户的注意力,哪些内容只需静静躺在归档里即可。
4.3 噪声过滤:把广告、软文和站内通知挡在门外
新闻源里混着大量不是新闻的内容:站内公告、活动报名、SEO聚合页、甚至"关于我们"这类固定页面。如果管道把这些都当成新闻入库,后端就会被迫做一次又一次的人工清洗。我的经验是做一个黑名单规则引擎,用域名+URL模式双重匹配的方式在入库前拦截。
具体来说,规则引擎对URL路径做模式匹配,比如/about、/contact、/tags/、/author/这些路径,一律跳过。另外维护一个关键词表,命中"广告""报名""抽奖""征稿"等特征词的文章,在标题层面就标记为低质量。
这一步的实战价值很容易被低估。我曾经测试过,不经过任何过滤直接抓某个综合新闻源的RSS,24小时内入库的两千多条记录里,真正值得留存的核心新闻不足六成,剩下的四成里一大半是营销内容。做了上述规则过滤之后,噪音比例可以压到两成以下,而规则本身只有几十行代码。
5. 合规与长期运营:爬新闻不难,难的是持续不掉线
新闻内容获取项目中,最容易被忽略但最终决定项目寿命的,是合规和运维这两个"非技术"问题。
5.1 版权与使用边界:抓取不等于拥有,存储不等于可以分发
这一点我必须放在最前面说:抓取新闻内容不等于拥有了内容的所有权。对个人使用场景,比如自己做阅读器、做归档,绝大多数站点实际上是宽容的;但如果要做成对外提供的服务,哪怕是免费服务,也需要确认目标站点的版权政策和平台条款。
我自己执行的一个红线是:**不做付费墙内容的绕过,不反编译移动端接口,不伪造身份绕过登录态。**新闻行业的付费内容是有明确商业边界的,突破这个边界会给项目带来巨大的潜在风险。另一个原则是,对外展示新闻时尽量做"摘要+原文链接"的形式,而不是把全文完整转储到自己的服务器上再分发。
此外,聚合展示时应该清晰标注来源站点和原文链接,这是新闻行业的通行惯例,也是让源站愿意容忍你采集的前提。哪怕是个人项目,我也会在数据模型里单独保留source_url和source_site两个字段,并在任何展示界面输出它们。
5.2 管道健康监控:你不可能永远手工盯数据
自动采集管道部署之后,最大的风险是"悄悄死掉"。目标站点改版导致选择器失效、RSS源突然停止更新、服务器端把请求频率限制收紧——这些故障往往在发生数小时甚至几天后才会被人察觉。如果阅读器展示的是已经三天的旧闻,那这个产品就失去了核心价值。
我给自己的管道加了一套极简监控:每轮采集结束后,对比本轮新增文章数与历史同时间段均值。如果新增数量骤降超过70%,或者某个来源连续三轮无新增,就触发告警推送到内部群。这个监控逻辑不依赖任何外部服务,用几行代码加一个定时任务就能实现。
同时,每个来源的解析代码和请求参数需要做版本管理。目标站点改版后,你会想快速回退到上一个能正常工作的版本,这时候用版本号管理配置就特别重要。我在项目里给每个采集源维护了一个JSON配置,字段包括enabled、request_interval、parser_version、last_success_time,出了问题第一时间排查配置变更历史,而不是在代码里大海捞针。
5.3 数据清洗与生命周期管理
最后一个容易被忽略的模块是数据生命周期。新闻数据的价值随时间迅速衰减,如果永久保存全部原始HTML,存储成本会持续失控。我的方案是:热数据保留近30天的全文,冷数据只保留标题、摘要、链接、发布时间这几个核心字段,原始HTML压缩后归档到对象存储。这样既保证了历史检索的基本需求,又把高成本存储控制在一个合理范围内。
从实战经验来看,一个长期稳定运行的新闻内容获取系统,技术选型反而是最不需要纠结的部分。真正决定系统寿命的是三件事:请求节奏是否克制、内容过滤是否持续迭代、故障是否能在几小时内被发现。把这三点做好,哪怕用的是最简单的技术栈,系统也能安安稳稳跑上几年;任何一点疏忽,哪怕用了最花哨的架构,也会在实际运营中被持续消耗。
我个人在跑这套系统的过程中,最大的体会是新闻数据和其他数据不一样,它天然有生命周期,也有天生的版权约束。做这个方向最有成就感的事,不是抓了多少条数据,而是站在用户视角把真正值得关注的信息从噪音里捞出来。如果你也在搭类似的东西,建议从最小的RSS源开始,先把管道跑通,再去扩充来源。跑通了增量更新和去重这两个关键模块,后面加再多的网站都不会太难。