你每天用在刷新闻上的时间有多少?半小时?一小时?如果是做投资研究、产品调研或者行业观察,这个时间还要翻倍。我决定做一个"关键词新闻简报"小工具:输入一个关键词(比如AI创业),通过调用新闻API自动抓取最新行业新闻,按时间排序后清洗、去重、抽摘要,最后生成一份可直接在手机上读的每日简报。跑了一个多月,每天稳定输出,省下来的时间非常可观。这篇文章把整个思路、选型、代码实现和踩过的坑都整理出来,适合想批量跟踪行业动态,又不想天天泡在资讯流里的开发者、自媒体编辑和研究者参考。
先说结论:这个工具的技术门槛并不高,真正的难点在于新闻源的质量控制、关键词的误召回处理,以及简报的可读性设计。如果只做一个"爬接口+打印标题"的脚本,十分钟就能写完,但那种东西基本没法坚持用。我折腾了几版,最后沉淀下来的方案是"API抓取 + 多级过滤 + 摘要生成 + 定时投递",下面按照实际开发顺序拆开讲。
1. 整体设计与思路拆解
1.1 核心需求与适用场景
标题里写的"调用新闻API,输入关键词,抓取最新行业新闻,生成每日新闻简报",落点其实是三个动作:定向采集、时效过滤和结构化输出。
我最开始只想要一个简单的"关键词搜索"工具,用一次调接口返回几十条结果就够了。但用几天后发现,零散查询解决不了"持续跟踪"的问题。真正的需求往往是这样:我想连续追踪"AI创业"这个概念,关注它每周的新获投融资、新产品发布、头部公司的起落,偶尔还要留意政策变化。这种需求靠人肉在搜索框里点,状态很难保持;靠搜索引擎的Alert邮件,噪音又大,邮件容易被淹没。于是做每日定时简报就成了最合理的形态。
适用人群也很明确:做竞品分析和行业研究的人,每天需要扫一遍某垂直领域;自媒体团队需要一个低成本的选题雷达;个人投资者跟踪特定赛道;还有像我这种"收藏了一堆资讯App但从来不打开"的人。本质上,这个工具解决的是信息筛选的重复劳动,把"每天花一小时看资讯"压缩成"每天用五分钟读简报"。
1.2 方案选型:为什么用新闻API而不是自己爬
这是第一个必须想清楚的问题。有人在设计阶段可能会说"直接去目标站点爬不就行了,又不要钱"。我确实也走过这条路,简单说几个实测教训:
- 头部新闻站的反爬策略更新很快,今天能用的请求头明天就可能返回验证页,维护成本远超预期。
- 新闻网站的结构频繁调整,昨天能解析的HTML,今天可能改了一个class名称就全部失效。
- 爬虫在法律风险上相对模糊,个人学习用还好,一旦做成团队工具或内部服务,版权和合规问题就要认真对待。
新闻API的优势在于数据的可获得性和规范性。它把新闻源聚合、结构化解析、时间戳统一这些脏活都替你做了,返回的JSON里通常包含了标题、正文摘要、发布时间、来源、作者、分类等清晰字段。你只需要做"业务逻辑",不需要处理"网页解析"。代价是收费和服务限制,但现在不少厂商都有免费额度,个人使用完全够。这里只分享选型原则,不点名具体服务商,因为接口服务变化太快,今天好用的明天可能就被收购改名了,读者只要拿着挑选标准去对比就行。
1.3 整体流程与各环节的取舍
整个简报生成流水线长这样:
关键词定义与过滤规则配置 → 定时触发抓取任务 → 调用新闻API获取新闻列表 → 数据清洗与去重 → 相关性打分 → 提取摘要 → 渲染成简报模板 → 输出或推送
这个流程里有几个容易被忽略的取舍点:
- 抓取频率定为每天一次,而不是实时。行业新闻不是股票行情,用户真正需要的是"今天发生的重要事情",而不是"这几分钟内的突发事件"。一天一次可以把API用量降到最低,免费额度完全覆盖。
- 时间窗口设为过去24小时,而不是过去1小时或3小时。考虑到各新闻网站发布时间和API收录之间往往有几个小时的延迟,如果只取最近1小时,早上看简报时可能漏掉昨晚黄金时段发的新闻。24小时窗口比较稳妥。
- 先把"摘要生成"放在第三步而不是第一步。很多新闻API的返回内容里已经附带一小段摘要,先用这段原始摘要做粗筛,如果足够,就用原文;用大模型做二次摘要只是为了解决那些"摘要缺失或标题党"的条目。
我把整个流程总结成一张职责表,后面每个模块都按这个职责去写。
| 模块 | 职责 | 输出形态 |
|---|---|---|
| 定时调度 | 每天在设定的时间点触发任务 | 触发信号 |
| 抓取器 | 组装参数、调用API、处理分页 | 原始新闻条目 |
| 清洗器 | 去重、过滤噪音、过滤旧闻 | 干净的新闻列表 |
| 打分器 | 计算关键词相关度、综合排序 | 带分数的新闻列表 |
| 摘要生成 | 提取核心信息、压缩成简报 | 摘要文本 |
| 渲染器 | 按模板输出Markdown/HTML | 最终简报 |
这个架构的好处是模块之间解耦,今天用这个API,明天换另一个API,只需要改抓取器,其他部分全都不用动。我在后续扩展多关键词的时候也体会到了这个好处:一个关键词一套Filter规则,共用同一套渲染模板,加一个关键词只是加一行配置的事。
2. 核心细节解析与实操要点
2.1 新闻API选型:不看品牌看这几个指标
市面上的新闻API五花八门,有的主打全球新闻聚合,有的专注财经快讯,有的其实是通用搜索引擎的新闻模式接口。我建议按五个维度去评估:
- 内容覆盖范围:这个接口到底聚合了哪些新闻源?能不能覆盖你关心的区域和语种?拿"AI创业"来说,国内项目和海外项目完全是两个圈层,如果你不想漏,可能需要配两个接口源。
- 时间精度与收录延迟:新闻发布时间字段是否精确到分钟?API端收录新闻的延迟通常不是实时的,有些轻松平台的数据要晚一两个小时。测试方法很简单:抓一个结果,打开源文章链接对一下发布时间。
- 字段完整性:除了标题和链接,是否返回正文摘要、作者、关键图片、来源Logo?字段越全,后面做渲染就越省事。最影响体验的字段其实是"摘要",没有摘要就没法做高密度简报。
- 配额与限流策略:免费次数是一次性赠送还是每天重置?并发限制多高?在开发调试阶段,这个限制决定了你能不能反复试错。建议选"按天重置"的,而不是"总量赠送"的。
- 是否支持关键词组合查询:有些接口只支持简单的短语匹配,有些支持 AND/OR 逻辑、站内源过滤、排除词。组合查询能力越大,生成日报的准确率越好。
2.2 请求参数设计:不是只传一个关键词就完事
如果调用新闻API只传一个q=AI创业,通常回来的结果是没法直接用的。原因是关键词匹配太粗,很容易召回到"某AI创业大赛报名"、"高校举办AI创业论坛"这种边缘内容,而且不同新闻源的发布时间格式不统一,后面的排序会乱。
我在实际配置中至少会传这几类参数:
| 参数 | 作用 | 我的建议值 |
|---|---|---|
q | 关键词或短语 | 根据过滤规则扩展,如"AI创业" OR "AI 创业公司" |
time_range | 时间过滤 | 24h |
language | 语言过滤 | zh或en |
sort_mode | 排序方式 | date或recency |
page_size | 每页条数 | 50 |
offset | 分页偏移 | 第一次为0,循环拉取 |
source_include/exclude | 限定源 | 过滤掉明显低质或不可靠的来源 |
这里有一个非常关键的点:关键词要做"正向扩展+反向排除"。比如追踪"AI创业",正面是"AI创业、人工智能创业、AI startup"等;反面要排除"创业大赛、创业培训、创业孵化器活动报名"等,因为这些关键词里虽然带"创业",但和"AI创业公司"的产业动态不是一回事。很多新闻API支持布尔查询,尽量用起来:
("AI创业" OR "AI 创业" OR "AI startup") AND NOT ("创业大赛" OR "创业孵化" OR "创业培训")实际编码时如果API不支持复杂语法,就在本地做二次过滤,规则放配置文件里,慢慢积累。
2.3 去重与噪音过滤:简报质量的生命线
这是我做了三版之后觉得最值得花时间的地方。新闻API返回的数据经常存在三个问题:
- 同一新闻被多家媒体转载,标题只改了几个字,链接完全不同,但内容几乎一样。
- 老新闻被翻出来重发,时间戳被更新,看起来像是"最新消息",其实内容已是几周前的旧闻。
- 关键词是泛匹配,"AI创业"会命中大量"AI+创业"但不相关的周边新闻。
我的处理方案是三段式:
第一步,标题归一化去重。把标题里的中英文标点、多余空格、"独家"、"快讯"等前后缀清掉,再把"AI人工智能创业"和"人工智能AI创业"这类词序不同的标题做字符集归并,转成小写、去掉空格和标点,然后按MD5算指纹,在当天已经收集的新闻里查重。
第二步,正文片段查重。因为标题去重无法拦截"改头换面"的转载,我把API返回的摘要取前60个字和已有新闻做SimHash或Jaccard相似度计算,相似度超过0.68就判定为重复,保留来源权重较高的那一条。我自己写的简化版是:把摘要切词后取集合交集除以并集,简单粗暴但是有效。
第三步,白名单和黑名单。小成本做法是只保留allowed_domains白名单内的来源,比如你自己信任的几家主流媒体和行业垂直媒体;再维护一个blocked_keywords黑名单,比如"广告"、"招聘"、"会议报名"等。这个名单一开始只有七八条,跑一周后慢慢扩充到几十条,新闻噪音明显下降。
2.4 相关性打分:别把"热度"当一切
API返回结果里通常会提供一个热度值或相似度分数,许多人直接按它排序。我的经验是:接口的热度分大多基于全网搜索指数和点击量,呈现明显的"头部马太效应"——大热点排前面,小而重要的行业事件会被淹没。于是我自己加了一个轻量级相关性打分公式,综合三部分:
- 关键词命中位置:标题命中加3分,摘要前80字命中加2分,正文命中加1分。
- 时效权重:发布至今在6小时内的加2分,12小时内加1分,超过24小时不加分。这个权重防止"分时段新闻"大量堆在日期排序前列。
- 来源权重:行业垂直媒体和权威媒体有基础分3分,一般媒体1分,被列入黑名单的直接排除。
总分计算:
score = 0.6 * 关键词命中得分 + 0.3 * 时效得分 + 0.1 * 来源基础分最终按分数从高到低取前20~30条进入简报。用这种方式排出来的结果,比单纯按API默认排序更贴近"行业观察者想看到的东西"。
3. 实操过程与核心环节实现
3.1 工程目录与模块划分
我把代码按职责拆成了以下结构。这套结构不复杂,但每个文件职责清楚,单测也好写:
news_digest/ ├── config.py # 关键词、API配置、黑白名单 ├── fetcher.py # 调用新闻API、解析JSON、处理分页 ├── cleaner.py # 去重、过滤、归一化 ├── ranker.py # 相关性打分 ├── summarizer.py # 摘要生成(优先用API自带摘要) ├── reporter.py # 渲染Markdown/HTML简报 ├── scheduler.py # 定时任务入口 ├── output/ # 生成的简报文件 └── data_cache.json # 每日去重指纹缓存config.py用来保存所有易变的配置,思路是"把变化留在配置文件里,而不是代码里"。今天追踪AI创业,明天追踪光储充,只需要改配置项,脚本主干都不用动。这样一个脚本可以同时服务多个关键词,配置结构大概长这样:
# config.py KEYWORDS_CONFIG = [ { "query": '"AI创业" OR "AI 创业" OR "AI startup"', "exclude_keywords": ["创业大赛", "创业孵化", "创业培训", "报名"], "language": "zh", "time_range": "24h", "page_size": 50, "max_items": 20, "output_name": "ai_startup_daily" }, { "query": '"光储充" OR "光储一体化"', "exclude_keywords": ["光伏安装工", "招聘"], "language": "zh", "time_range": "24h", "page_size": 50, "max_items": 15, "output_name": "ess_daily" } ]3.2 抓取器:处理分页和时区的关键代码
写抓取器时最容易翻车的三件事:分页循环条件写错、时间字段时区没有转换、响应结构变化没有兜底。我直接贴一个可用版本,关键点写在注释里。
# fetcher.py import requests import time from datetime import datetime, timedelta, timezone def build_params(query: str, language: str, time_range: str) -> dict: """组装请求参数,注意查重、排除词可以放进query""" # 这里用的是占位域名,实际项目替换为自己的API端点 return { "q": query, "language": language, "time_range": time_range, "sort_mode": "recency", "page_size": 50, "offset": 0, "apikey": API_KEY, } def fetch_all_news(query: str, language: str, time_range: str, max_pages: int = 3): """拉取入关键词相关的新闻列表,带分页保护""" all_items = [] offset = 0 for _ in range(max_pages): params = build_params(query, language, time_range) params["offset"] = offset resp = requests.get( "https://api.example.com/news/v1/search", params=params, timeout=15, ) data = resp.json() items = data.get("articles", []) if not items: break # 把时间字段统一转成北京时间,简版适配两种常见格式 for it in items: raw_time = it.get("published_at") or it.get("timestamp") it["pub_time"] = normalize_datetime(raw_time) all_items.extend(items) # 如果回传总量已经大于当前列表,说明还有下一页 total = data.get("total", 0) offset += len(items) if offset >= total or offset >= 100: break time.sleep(0.3) return all_items def normalize_datetime(raw_time): """把不同格式的发布时间统一为 UTC+8 时间字符串""" if isinstance(raw_time, (int, float)): return datetime.fromtimestamp(raw_time, tz=timezone(timedelta(hours=8))) # 字符串形式按ISO格式解析成UTC再转北京时间 text = str(raw_time) dt = datetime.fromisoformat(text.replace("Z", "+00:00")) return dt.astimezone(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M")分页这里有个细节:不要把max_pages设太大。我试过把前500条全部拉回,然后发现大量长尾噪音会让打分器无所适从,而且免费API的配额会被一次任务消耗一大半。现在策略是:最多翻3页,每页50条,即最多拉150条,足够覆盖一天的关键新闻。
3.3 清洗与过滤:指纹去重和关键词排除
清洗器负责把原始数据变成真正进入排行榜的候选。去重我用了一个每日指纹缓存文件data_cache.json,这样跨天任务之间也能共享历史指纹,避免同一新闻在连续两天的简报里重复出现。实现思路是这样的:
# cleaner.py import hashlib import json import re def normalize_title(title: str) -> str: """去掉标题中的符号和语气词,生成归一化文本""" title = title.lower() title = re.sub(r"[\s\-\|\·\:_()()【】\[\]{}]+", "", title) title = re.sub(r"^(独家|快讯|最新|重磅)", "", title) return title def make_fingerprint(item: dict) -> str: """优先用标题归一化后的指纹去重""" norm = normalize_title(item.get("title", "")) return hashlib.md5(norm.encode("utf-8")).hexdigest() def is_duplicate(item: dict, cache_data: dict) -> bool: fp = make_fingerprint(item) if fp in cache_data: return True # 再判断摘要相似度,用来拦截改标题的转载 summary = item.get("summary", "")[:100] for old_fp, old_summary in cache_data.get("summary_fingerprints", []): if jaccard_similarity(summary, old_summary) > 0.68: return True return False def jaccard_similarity(text_a: str, text_b: str) -> float: set_a = set(text_a.split()) set_b = set(text_b.split()) if not set_a or not set_b: return 0.0 inter = set_a & set_b union = set_a | set_b return len(inter) / len(union)这里最值得说的是jaccard_similarity的阈值。我一开始定0.5,结果很多正常新闻被误杀;后来调到0.8,重复转载又漏得厉害。反复测试之后0.68是个不错的中点。如果你的新闻源转载现象特别多,推荐用SimHash做64位指纹,Hamming距离小于3认为是重复,误判率会更低,不过开发成本也高一些。
能被黑名单排除词命中的条目,直接在清洗阶段扔掉。比如标题里出现"报名"、"开奖"、"倒计时",大概率是活动通告而不是产业新闻,这在行业观察简报里属于噪音。
3.4 摘要生成:先用原摘要,再谈大模型
很多新闻API返回的summary字段已经概括了正文核心,足够做成简报。但对一些缺摘要的条目,或者摘要本身就是"一句话宣传文案"的情况,就需要生成式摘要。我的实际做法分成两层:
- 第一层:如果API自带摘要长度大于60字,直接使用。好处是零成本、速度快。
- 第二层:对摘要过短或明显是营销文案的内容,再把原文标题和首段文本送入大模型,让模型生成不超过两句话的摘要。
这里我不建议每次跑完150条全部请求大模型,成本高且速度慢。正确的打开方式是只对进入前十排序的候选做精修摘要。用大模型的时候,Prompt要给出非常明确的指令,不然模型天然喜欢"虚写"。一个可复用的Prompt模板:
请根据提供的新闻标题和正文片段,生成一句简洁、客观、信息完整的摘要。 要求: 1. 包含核心主体、关键数据、时间。 2. 不使用比喻和夸张修辞。 3. 不超过60个字。 4. 输出结果为纯文本,不要添加任何前缀。 【标题】{title} 【正文片段】{snippet}如果厂商支持结构化输出,优先用JSON模式,拿{"summary": "..."}会稳定很多,不然模型偶尔会加一句"该新闻主要报道了..."这种废前缀。拿到摘要后,我还会在简报里保留原始标题,不直接用生成的摘要替代标题,因为标题往往是原文来源的表达方式,有利于用户点击判断可信度。只有当我发现点击率评估加权后效果更差,才换用模型重写的短标题方案。目前保留原标题体验更好。
3.5 渲染模板与简报效果
渲染器负责把清洗好的新闻列表变成Markdown和HTML。我比较喜欢用一个紧凑的模板,每一条包含:序号、标题、来源、发布时间、摘要、原文链接。头部信息记录生成时间、关键词、新闻条目总数,这样拿到简报能一眼看出这是哪天的,覆盖了多少信息。
# reporter.py def render_markdown(news_items, keyword, generated_time): lines = [ f"# {keyword} 每日简报", f"生成时间:{generated_time} | 覆盖新闻:{len(news_items)} 条 | 统计周期:24小时内", "", "---", "", ] for idx, item in enumerate(news_items, 1): lines.append(f"**{idx}. {item['title']}**") lines.append(f"> {item['summary']}") lines.append(f" 来源:{item['source']} | {item['pub_time']} | [阅读原文]({item['url']})") lines.append("") return "\n".join(lines)实际产出的效果像是这样(标题和内容均为模拟示例,用于展示排版效果):
# AI创业 每日简报 生成时间:2025-06-12 08:00 | 覆盖新闻:23 条 | 统计周期:24小时内 --- **1. 某AI初创企业完成新一轮1.2亿元融资,聚焦企业级智能客服** > 该公司将把资金用于底层模型优化及销售团队扩张,本轮由老股东持续加注。 来源:某行业媒体 | 2025-06-11 21:30 | [阅读原文](https://example.com/xxx) **2. 大模型创业公司加速出海,东南亚成热门落地场景** > 多家AI公司本季度在东南亚推出本地化产品,主要涉及语音客服和智能文档处理。 来源:某科技网 | 2025-06-11 19:00 | [阅读原文](https://example.com/xxx)可以看到整份简报能在一屏内读完,重要信息密度很高。HTML版本会额外加图片和摘要标签样式,但内容完全一致。我实测下来,Markdown在微信收藏、飞书文档、邮箱里都比较友好,HTML则适合放到团队内部知识库。
3.6 定时执行:三种方式,按成本选
定时执行是整个"每日"的关键。我试过三种方式,各有使用场景:
- 本地环境 + 系统cron:最简单,直接在服务器上写一个cron,每天早上8点运行一次
python reporter.py。适合有常驻Linux机器的人,缺点是机器不能关机,如果断了电当天简报就没了。 - GitHub Actions / 类似CI定时任务:把代码推到一个私有仓库,在workflow里配置
schedule: cron表达式,每天触发。适合不喜欢买服务器的开发者,免费的分钟数对一天一次的任务完全够用。但要注意时区问题,cron表达式默认UTC,要换算到自己的时区。 - 云函数/定时触发器:把抓取和渲染逻辑封装成无服务器函数,权限配置好后定时触发,没有服务器运维负担,但需要额外学习平台配置。
我自己目前用的是云函数加定时触发器,原因只有一个:不想维护一台常驻机器。部署时要用环境变量保存API Key,不要把密钥写进代码仓库。云函数的冷启动对这类任务没有影响,每天8点触发,早高峰可以接受。如果你准备用cron,我的建议是执行时间选北京时间早晨8点,而不是凌晨0点。0点跑出来的新闻量太少,因为很多媒体当天新闻集中在上午发布,8点跑刚好收集昨晚和今早的重要动态。
4. 常见问题与排查技巧实录
4.1 高频问题速查表
把这一个多月的使用过程中我遇到的复现率高的故障整理成了一个速查表,方便你直接对症下药。
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 请求返回401 | API Key写了但请求参数里没有传,或Key已过期 | 检查请求参数的认证头,打印完整URL验证 |
| 返回乱码 | 接口返回编码不是UTF-8,但脚本按UTF-8解析 | 强制resp.encoding = "utf-8",必要时指定其他编码 |
| 发布时间比当前时间晚 | 时区没有转换,接口返回UTC当成本地时间 | 统一转成带时区的时间戳后格式化 |
| 同样的新闻连续两天出现 | 去重指纹只在任务内有效,跨天没有缓存 | 启动时加载data_cache.json,结束后写回 |
| 某天导入的关键词没有任何结果 | API免费配额耗尽,或该词确实没有当日新闻 | 查看接口配额余量,换相近关键词定位问题 |
| 简报里出现明显不相关的条目 | 关键词泛匹配,没有排除词 | 扩充排除词库,降低关键词命中得分的低分权重 |
| 定时任务没跑起来 | cron时区错误或表达式写反 | 先用代码内置一个"测试任务",跑一次看日志 |
4.2 我踩过的一个经典坑:分页中断导致新闻缺失
我第一次上线后,发现简报里某几个来源的新闻占比异常高,检查日志发现是因为在分页请求中,有一页返回超时,脚本没有重试直接break了,导致后面的全部新闻根本没进列表。这个bug最坑的地方是单次运行看不出来,因为大多数时候第一页就够用了;但遇到热点日新闻量暴涨时,第一页和第二页之间就会暴露问题。
修复方案:给分页循环加上retry机制,单页最多重试2次,超时时间增加到20秒;如果重试仍失败,把这一页的查询参数记录下来,生成简报时在末尾标注"部分新闻源响应超时,信息可能不完整"。有了这个降级提示,简报的可信度反而更高。
4.3 排错时的三个好习惯
- 保留原始响应样例。把API返回的JSON写入本地文件,保存最近三天的数据。这样当简报出现异常时,可以回看是"接口数据变了"还是"自己处理逻辑出错了",不用再手动调用一次。
- 给每一条新闻增加来源URL的日志。以前出问题只能看到一个标题,想知道它从哪来就得再搜一遍。后来我在调试模式下把URL、发布时间、API原始分数全部打印出来,问题定位效率提升非常明显。
- 谨慎修改时间范围。不要在调参的时候把
time_range从24h改成7d做测试,这会改变任务行为。应该新建一个调试配置,对正式环境零影响。
4.4 内容和运营层面的避坑
技术问题可以靠日志解决,但简报的可用性问题往往在运营层面。我用了一周后发现,单纯记录"行业新闻"是不够的,用户还是要点开原文才知道这件事和自己有什么关系。于是我在每一条新闻后面增加了一个why字段,由代码根据关键词预先写好一句"关注点提示"——比如今天是"融资"相关新闻,就在标题下面加一行"提示:关注下轮估值与核心投资方"。这个why字段一开始是静态配置,后来发现用规则模板生成的效果已经不错,不必每篇都上大模型。
另外,要给简报设置"硬上限",我通常控制在25条以内。超过这个数,阅读负担会直线上升。宁可每天只挑出最重要的几条,也不要追求"全而多"。用户的耐心很有限,一份能长期坚持读下去的报告,一定是"少而精"的。
5. 进一步扩展:多关键词、多推送与个性化
如果只想做最简版,看到第4章就可以收工了。但如果想让这个工具真正变成团队或个人助理,下面几条是我认为性价比最高的扩展方向,按开发成本从低到高排序。
- 多关键词合并输出:在配置里面增加更多关键词,把多份结果聚合到同一份简报里,用分节呈现。这对研究"AI创业+光储充"这种"跨界观察"特别有用,可以看到两个趋势在同一时期的交集。
- 推送渠道扩展:把Markdown渲染成纯文本后,可以接入邮件、IM群机器人、知识库接口。最省事的是先做邮件,因为SMTP协议最通用;IM机器人接收体验好,但需要申请机器人权限和环境变量。注意不要把密钥放在代码里,统一走环境变量。
- 新闻源加权:如果你明显觉得某些垂直媒体的报道质量高于门户聚合,可以给那些来源加一个
quality_weight,在打分环节放大。这个参数放在配置里,方便随时调。 - 周末模式与假期模式:周六日的财经科技新闻量会明显下降,可以设置成"如果没有超过3条高分新闻,就不发简报"的省电模式。否则周末发一份只有两条新闻的简报,反而会让用户对这个工具失去信心。
- 历史归档与趋势统计:把每天的简报文件按日期归档,积累一个月后就能分析"这周和上周比,哪些关键词出现频率升高了",相当于做一个不带模型训练的舆情指数。
我最近正在做的是把data_cache.json升级成SQLite,这样不只是去重,还可以做简单的关键词热度统计。这是很自然的演进路线,文本缓存适合前期,SQLite适合长期维护。
写在最后的开发体会
做这类"小而实用"的工具,技术实现占四成,剩下六成是内容策略。我发现真正决定它能不能长期坚持用下来的,不是模型能力有多强,而是噪音控制得有多好。能坚持每天读一份简报,是因为它每一篇都值得点开;一旦出现两三天的连续低质量推送,用户就会把它关进收藏夹遗忘掉。
最后分享一个我每天都在用的小技巧:在每个关键词的简报开头,自动加一行"今日信号强度",规则是根据当天新闻总数、高分新闻占比、是否有权重媒体来源,生成一个1到5的星级评价。信号强度高的日子,我会花更多时间深挖;信号弱的日子,5分钟扫完即可。这个细节虽然只有一行代码,但它让整个工具从"信息堆砌"变成了"主动告诉你今天值不值得关注"。如果你正在做类似的东西,强烈建议把这个小点子加进去。