☰
从信息过载到主动获取:本地优先AI资讯浏览器插件实践
2026/10/20 5:14:36 网站建设 项目流程

每天都在被 AI 资讯淹没,这种感觉我太熟了。从去年开始,我关注的中文 AI 领域的公众号、知识星球、论坛帖子加起来得有几十个,每天早上打开手机,光是未读的“重磅”“震惊”“突发”就够我刷半小时。更夸张的是,等我把这些文章刷完,往往发现真正有价值的信息就那么两三条,剩下的全是包装过的重复内容或者公关稿。

后来我实在受不了这种被动接收的模式,干脆动手做了一款本地优先的浏览器插件,让我关注的 AI 情报自己去“找”我,而不是我每天像一个没有感情的转发机器一样,在信息的垃圾场里翻找金子。

这篇文章就把整个项目的来龙去脉、设计思路、踩过的坑、以及最终的落地实现,全部一次性讲清楚。不管你是被信息焦虑困扰的普通读者,还是想自己动手做点工具的开发者,应该都能从中拿到不少可以直接用的东西。

1. 这个插件到底解决了什么问题

先说痛点。中文 AI 资讯领域有个很特别的现象:信息密度极高,但噪声更大。今天这家公司发布了新模型,明天那家机构放出了评测报告,后天又有人开始炒作某个新概念。你要是完全跟风,很容易被带着跑;你要是不看,又怕漏掉真正重要的技术拐点和行业信号。

我最初试过好几个方案。订阅 newsletter,很多高质量的中文内容根本不在邮件里;用 RSS,越来越多的平台砍掉了源地址;靠社交媒体算法,那更是个吞时间的黑洞,刷二十分钟连自己最初想看什么都忘了。于是我换了个思路:既然信息源分散在各个网页、各个平台,那我为什么不直接在浏览器这个环节上做文章?毕竟我日常获取内容的入口,最终都会落到浏览器的页面上。

所以这个插件定下来的核心定位就是三个词:本地优先、主动聚合、低打扰。

本地优先意味着你的浏览数据、订阅配置、筛选规则,全部存储在浏览器本地,不走云端服务器,我作为开发者都看不到你的数据。主动聚合是指插件会在后台按你设定的节奏抓取你指定的信息源,然后生成一份“情报摘要”。低打扰是说它不会像某些助手一样动不动就弹窗刷存在感,而是等你主动打开新标签页的时候,才告诉你“你关注的这几个源今天更新了这些内容”。

用一句话概括:它不是一个更花哨的阅读器,而是一个帮你把信息获取成本降下来的过滤器和提醒器。

2. 整体方案设计:从信息源到最终呈现要过几道关

信息处理流程是这次设计里最核心的部分,整体可以拆成四个阶段。

第一阶段是信息源管理。我先预设了一批质量相对稳定的中文 AI 信息来源,包括几家头部科技媒体的官网、几个圈内认可度较高的个人博客、以及一些文档更新频繁的官方发布页面。你在设置界面可以勾选或取消,也可以手动添加自己的源。这里有个细节:我保留了一个“自定义选择器”的输入框,方便你为某个特定页面配置内容抓取规则。

第二阶段是内容抓取。插件在后台通过 fetch 请求去拉取目标页面的 HTML,然后解析出标题、摘要、链接、发布时间。这一步碰到的阻力不小,后面我会专门说怎么应对各种反爬策略和页面结构差异。

第三阶段是兴趣过滤。这是整个插件和普通 RSS 阅读器拉开差距的地方。不只是按关键词匹配,我加入了“信号积分”机制。比如标题里出现“发布”“开源”“融资”“论文”这类强信号词,权重加高;出现“教程”“思考”“观点”这类弱信号词,权重降低。同时可以叠加自定义关键词的黑白名单。当一篇文章的信号积分超过阈值,它才会进入最后的待推送队列。

第四阶段是摘要与展示。插件在本地调用一个抽取算法,从正文里提取关键句子,生成一段 150 字左右的摘要。配合原文链接、热度标记和状态标签,在一块独立的面板里呈现。你每打开一个新标签页,面板自动更新一次。

这里我刻意没有引入后端服务。所有抓取和解析都在浏览器扩展的 service worker 和 content script 里完成。好处很明显:部署成本低,你不需要维护服务器;隐私性好,配置和数据都在本地;轻量,几十 KB 的扩展就能跑起来。代价也有:跨域限制更严、无法很轻松地做大规模并发抓取、规则的更新得靠插件版本迭代来推。对于个人使用场景,这个取舍是值得的。

3. 技术栈选型与关键依赖的取舍

插件基于 Manifest V3 规范开发。这个选择是必须的,Chrome 已经在逐步淘汰 V2,新上架的扩展从审核逻辑上就更倾向 V3。在 V3 里,后台逻辑被定义为 service worker,它是事件驱动、可被随时回收唤醒的,生命周期比 V2 的常驻后台页要碎片化得多。一开始我还担心这个限制会影响定时抓取任务,实测下来发现,只要设计好唤醒和注册机制,问题不大。

技术栈上我没有引入前端框架,就是原生 JavaScript 加少量工具函数。整个项目的代码量大概在两千行左右,分成了 background.js、content.js、options.js 和 popup.js 四个主模块。数据存储用 chrome.storage.local,比 localStorage 更适合扩展场景,支持存对象、容量也大一些,而且能配合 chrome.storage.sync 做配置跨设备同步。

解析 HTML 的时候我用的是 DOMParser,没有挂第三方解析库。很多人会下意识地在扩展里引入 Cheerio,但浏览器的原生 API 足够处理绝大多数情况,引入额外依赖反而会拉大打包体积,增加权限审查成本。摘要生成我同样没有调 AI 接口,而是用“词频-逆文档频率”的思路提取关键句。这样做省了 API key,也保证了离线可用。

唯一让我犹豫过的工具是跨域请求的代理服务。真实抓取过程中会遇到 CORS 限制,尤其是从扩展的 background 去请求一个不相关的站点时。我一开始尝试用 Extension 的 host_permissions 直接申请目标域名权限,发现不少站点还是会在服务端校验 Origin,返回 403。后来我换成了通过 content script 在页面上下文里发请求,利用同源策略绕过去。代价是页面必须打开着才能触发,所以我设计了一套“后台预唤醒加页面静止抓取”的机制,后面详细说。

4. 核心细节拆解:配置项、抓取策略和信号积分算法

这部分是重头戏,也是踩坑最多的地方。

先看配置项。在 options 页面里,我把配置分为三类:信息源配置、抓取配置、展示配置。

信息源配置是一串 JSON 格式的订阅列表,每一项包含 id、名称、主页地址、抓取 URL 模板、内容选择器、标题选择器、链接选择器和时间选择器。可能你会觉得这么写配置有点复杂,但好处是灵活。针对不同的网站结构,可以做出差异化的适配。为了让非技术用户也能用,我写了一个可视化的“测试抓取”按钮,填完选择器之后可以直接预览抓取效果,不用盲猜。

抓取配置里最重要的两个参数是抓取频率和并发上限。抓取频率我默认设为 60 分钟一次,太频繁容易触发对方网站的限流,太低又会漏掉当日热点。并发上限默认 3,就是同时最多跑三个源的抓取任务,避免浏览器被一堆请求拖垮。这里有一个小细节值得养成习惯:所有请求都尽量带上合适的 User-Agent 和 Accept 头,有些站点没有这些头部会直接返回空结果。

展示配置相对简单,主要是摘要长度、面板位置、以及未读标记的样式。

再说信号积分算法,这是过滤环节的灵魂。我给每条被抓取到的文章算一个 score,公式是:

基础分 baseScore 为 20 分,每条强信号词加 25 分,每条弱信号词减 10 分,自定义白名单词每条加 30 分,黑名单词每条直接置为 0 分。强信号词你可以在预设列表里看到,比如“发布”“开源”“测评”“融资”“报告”“重磅”“突破”。弱信号词包括“教程”“复盘”“观点”“思考”“杂谈”“周报”。自定义词则由你在配置界面添加,实现完全贴合个人关注点的过滤。

举一个实际例子。某条标题是“某开源模型发布新版本,性能全面超越上一代”,里面有“开源”和“发布”两个强信号词,基础分 20 加 50,总分 70,超过我设定的默认阈值 50,会进入摘要区。“某某团队发布季度复盘,一些个人思考”里既有强信号词“发布”,又有弱信号词“复盘”和“思考”,得分是 20 加 25 减 20,等于 25,低于阈值,直接被过滤掉。

阈值也可以在配置里调,灵敏型用户设 30 就行,保守型可以调到 80。这里面包含一个很常见的误区:不是过滤得越狠越好,而是要在召回率和精准率之间做平衡。调太低你会看到一堆无关内容,调太高可能漏掉真正重要的信息。我自己最后用的是 55,同时把“重大发布”这类自定义词加进白名单,双重保险。

5. 从零实现完整抓取链路的过程实录

这一节我按真实开发顺序,把当初一步步跑通抓取链路的过程复盘一遍。这部分代码思路可以直接复用,很多细节都属于那种“没踩过坑绝对意识不到”的类型。

我的第一个版本抓取逻辑很简单:在 service worker 里直接 fetch 目标页面,然后用 DOMParser 解析。测试下来,正常返回的页面基本都能解析成功,但很快我就遇到了第一道坎:部分站点开启 Cloudflare 防护,或者做了 JavaScript 渲染依赖,直接拿到的 HTML 内容非常有限,列表区域的标题都是空的。如何处理?一种是依赖站点提供 JSON 数据接口,很多现代 Web 站点都会在 window 里注入一份嵌入了文章列表的 JSON 数据,解析这份数据要省力得多。另一种是改用 content script 方式,在页面已经加载完毕后再去提取 DOM。

我采用了双模策略。extension 优先尝试 fetch 解析,如果发现解析结果为空,再降级为 content script 模式。content script 模式这里我不展开讲协议细节,核心做法是让插件在后台探测到某个源需要脚本渲染,就自动打开那个页面,等渲染完成后从 DOM 里抽取数据,再把数据通过消息通道回传给 service worker。实测下来,这种方式能覆盖大约九成场景。

第二个坎是有不少站点加入了防盗链和 Referer 校验。直接 fetch 时如果 Referer 是空的,或者来自 chrome-extension:// 开头,请求很可能被拒。解决方法是在请求头里带上目标站点自身的首页地址作为 Referer。这个操作不会涉及到任何可疑手段,只是模拟一个浏览器内正常点击链接时应该带上的信息来源。

第三个坎是关于编码的。有些老牌网站还在用 GBK 编码,fetch 回来的文本默认按 UTF-8 解析,就会出现乱码。解决方案是读取 response 的 arrayBuffer,然后用 TextDecoder 来指定编码解析。我一开始扔掉了这一层,结果好多源的内容抓出来都是“锟斤拷”级别的乱码,排查了大半天才定位到问题。

第四个坎是去重。同一个源在不同入口页里可能反复出现同样的文章,或者不同源之间会互相转载同一篇内容。我的去重策略是取标题分词之后的前五个词,加上文章链接的域名,算一个哈希指纹。如果指纹相同,就认为是重复内容。虽然朴素,但实测效果不错。收藏夹里有个功能可以看到“重复来源”的列表,一眼就知道哪些站点是转发大户。

接下来是摘要生成。我的做法把正文分段后,按段落长度过滤掉太短的碎片,然后计算每个段落中出现在标题里的关键词的密度。密度最高的前三段作为候选,再各自截取前 150 字,去重拼接成摘要。整个过程不依赖第三方接口,全是纯本地运算。跑得很快,单篇文章大概几毫秒。

6. 呈现层的设计:如何让情报面板不打扰又实用

抓回来、过滤完、生成好摘要的信息,最后要通过一个合适的界面呈现。我选择的载体是新标签页替换页。每当你新建一个标签页,就能看到当天的情报面板,按“高信号”“中信号”“全部更新”三个分组展示。

这个设计的出发点很简单:新标签页是浏览器里最高频的被动触发场景,不需要用户去主动点开扩展图标。频率太高或太低都不行,只有当你主动要开始一次新的浏览任务时,它才出现一下。就像前台把当日快报放在你桌上,而不是每次路过都追着你念一段。

面板的视觉风格我做了很多轮调整。最初版本的信息密度太高,列表里塞了完整摘要、发布时间、来源域名、积分值、标签,看起来就像一个调试工具,毫无使用欲望。后来我改了设计原则:一屏只展示七个最重要的信息卡片,每条卡片默认只显示标题、来源和一句话摘要,完整摘要需要点击展开。积分值不直接显示,而是映射为一个三档热度条,避免用户还要去理解一个数字。

这里有一个交互细节值得单独拿出来说。卡片上的“标记已读”按钮,我不想让它变成一个孤立的操作,于是改成了滑动删除,左滑卡片标记已读,右滑卡片加入稍后读列表。这个交互是受到了邮件应用的启发,实测下来比单纯点按钮要自然得多。标记已读之后,该条记录就沉入历史档案,不再出现在当天面板里。

面板底部还有四个快捷入口:“刷新全部”“只看高信号”“历史归档”“设置”。“只看高信号”是我自己最常用的功能,配合信号积分机制,能快速切换到一种“只关注重大动态”的模式。如果你今天的状态不适合摄入太多信息,这个按钮就是你的安静模式。

7. 隐私安全与性能优化的实际考量

本地优先这四个字,在架构层面是怎么落实的?我梳理了几个关键点。

所有数据都存放在 chrome.storage.local,包括订阅配置、抓取结果、历史归档。插件没有自己的服务器,也不会把任何抓取到的数据上传到任何地方。唯一的网络请求是对你订阅源页面发起的抓取请求,以及抓取那些页面时携带的静态资源请求。你在隐私设置里可以看到一份完整的请求日志,记录每次请求发生的时间、目标地址和请求类型。

我不做任何形式的用户行为追踪。安装插件后不会有标识符生成,不会通过页面注入收集浏览历史,也不会在后台做遥测统计。你如果打开扩展的权限说明,会看到它只申请了 tabs 和 storage 两个权限。这里额外说明一下,我没有申请类似“读取全部浏览历史”的权限,那对于一个情报聚合工具来说完全没必要。

性能方面,最大的风险是后台抓取任务对浏览器续航的影响。我在设计时做了一组限制:抓取窗口集中在当日的几个整点附近,避免后台进程在随机时间点上频繁唤醒 CPU;每次请求之间加入随机 300 到 800 毫秒的延迟,而不是用固定间隔,这个随机化不仅是为了躲避限流,也是为了减小并发瞬间的电量冲击;所有抓取结果写入本地存储之前都会做字段裁剪,去掉无用的样式类名、内联脚本和图片原始数据,只保留纯文本和必要属性。

我还加了一个轻量级的缓存策略,针对更新频率低的源,抓取间隔动态拉长到 2 到 4 小时;针对资讯密集型的源,则维持 30 分钟一次的频率。这个动态调节的依据是这个源最近十次抓取结果的平均增量变化率。如果一个源每次都只有 1 到 2 篇新文章,就没必要让浏览器高频空转。

8. 常见问题与排查技巧实录

开发和使用过程中,我遇到了不少问题,有些凭直觉就能想到,有些则折磨了我好几天。这里挑几个有代表性的写出来,按“问题表现、排查过程、最终方案”三个部分来说,方便你直接对照。

第一个问题是某些订阅源一直抓取不到内容,但手动在浏览器里打开页面是正常的。排查过程是:先看扩展的 console 日志,确认抓取请求是否真的发出去了;再查看返回状态码和响应内容,发现返回的是 403。这说明请求被服务端拒绝了。原因是扩展的请求头里缺少一些关键字段,或者携带的 Referer 不符合站点预期。最终方案是在请求头部显式写入目标站点首页作为 Referer,并带上常规浏览器的 User-Agent 和 Accept-Language 头。这个问题修改后立刻解决。

第二个问题是抓取到的内容里 HTML 标签残留很多,摘要里会出现成片的样式字符串。排查过程是:我发现解析时直接用 innerHTML 取文本,把一批带有样式的节点也一并拿了进来。最终方案是在提取文本时先遍历 DOM,过滤掉 script、style、noscript 这类标签,再用 textContent 拼接;同时用正则把多余的连续空白字符压缩成一个空格。这里踩过的坑是直接用正则去处理 HTML,会越写越乱,千万不要图省事。

第三个问题是面板加载时偶发白屏。排查过程是:我的新标签页面板用了 document.write 来写入界面脚本,在某些加载时序下,脚本执行时机太早,导致 DOM 还没准备好。最终方案是把页面结构改成静态的 HTML 模板,脚本放在文件末尾等待 DOMContentLoaded 事件触发后再初始化。

第四个问题是部分站点反爬逻辑会在短时间内连续请求后弹出验证码,导致后续全部失败。排查过程是:验证码出现的频率和请求频率有明显的相关性。最终方案是加入抓取频率冷却机制,单个源一旦在三次请求中检测到异常响应,就暂停该源 30 分钟;同时把并发上限降到 2。相对于抓取时效性,稳定性和不被封禁显然更重要。

第五个问题是历史归档数据逐渐膨胀,本地存储容量报警。排查过程是:我查了 chrome.storage.local 的配额,发现虽然比 localStorage 大不少,但也不是无限。最终方案是给归档记录加了一个 90 天的自动清理周期,超过期限的数据会被异步清除。为了避免误删,清理之前会生成一份可导出的 JSON 备份文件。

下面整理成一份速查表,遇到类似问题的时候可以直接对照。

问题表现常见原因直接方案
部分源一直抓取失败缺少 Referer 或 UA 头请求头补全常规字段
抓取内容乱码页面使用 GBK 编码用 TextDecoder 指定编码
内容里带 HTML 标签提取逻辑未过滤标签过滤 script/style 后再拼接
面板偶发白屏脚本执行时序不对静态模板加事件监听
连续请求后出现验证码请求频率过高冷却机制加降并发
本地存储空间不足归档数据未清理90 天自动清理加导出备份

9. 从每日泛读到定向追踪的升级路径

插件跑到这个版本,基本满足了我的原始诉求:每天打开新标签页,如果当天有值得看的 AI 动态,面板会告诉我;如果没有,就安安静静地显示一句“今日无高信号情报”。

在实际使用了两周之后,我发现自己的阅读习惯起了变化。以前是早上被动刷一遍各个信息流,看到什么是什么。现在变成了主动在面板里挑两三条真正有价值的深度文章,剩下的时间用来做更重要的事。这种从“泛读”到“精读”的转变,在我看来是这个工具最大的价值。

后来我还做了一个小扩展:针对特定型号或者特定公司的动态,单独建立一个跟踪主题。比如你最近在关注某个开源模型的迭代周期,就可以新建一个主题,只订阅那些标题或摘要里同时包含模型名和发布类关键字的文章。一旦有新文章命中,面板顶部会出现一条主题横幅,点进去可以看到该主题的时间线。这个功能本质上是信号积分机制的一个结构化应用。

我也考虑过接入 RSS 的标准化解析,让通用性更高。实现上并不难,就是在 content script 之外再加一条基于 XML 解析的通道。但维护成本会比现在高不少,因为不同 RSS 源的字段风格差异还挺大。暂时把它放在待办清单里,等真正有需求再说。

如果你也想做一个类似的东西,我的建议是先不要追求功能大而全。把最核心的“抓取、过滤、展示”三条链路跑通,比什么都重要。后续再根据你的实际使用习惯,逐步加入新功能。很多功能不加之前你觉得很需要,加了之后才发现一年也用不到几次。少做无用功,是这类个人工具最值钱的理念。

10. 实际使用三周之后的个人体会

距离插件跑通第一版到现在,已经过去了大约三周。期间我调整了四次信号词的列表,把最初预设的二十多个词删到了十五个左右。删掉的那几个大多是描述性词汇,用处不大,反而容易把一些旧闻重新带回来。留下来的词则全部是强行动词,比如发布、开源、融资、关闭、退役。好的信号词,应该是对“事件已经发生”最直接的描述。

我每天早上看完情报面板的时间,从以前刷手机的半个多小时,缩短到了现在的十五分钟以内。这个时间节省,核心是过滤器在帮你做决策,而不是让你在信息流里自己挣扎。

还有一个我自己很喜欢的细节,是“午夜快报”功能。我设定了每天晚上十一点生成一份当天的信息汇总,直接存在本地。第二天早上打开面板时,会先看到这份快报,再看到昨晚新增的内容。这样就不会出现“早上只看到重庆发布完的内容,却没看到深夜发布的另一个公告”这类信息空档。

如果你打算自己动手做,最后分享一个小技巧:扩展的调试期一定要用真实场景的数据来测,别只用一两个你熟悉的站点头跑。我就是因为在开发阶段只测了三个优质源,结果上线后发现各种反爬和页面结构兼容问题接二连三地冒出来。每加一个新源都像一次小型的逆向工程,但每适配一个源,插件对你个人的信息生态匹配度就高一分。这种一点点把工具打磨成自己专属形态的过程,本身就是这个项目最有意思的部分。

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

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

立即咨询