news 2026/9/4 14:01:06

AI浏览器插件实战:用大模型整理B站收藏夹与关注列表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI浏览器插件实战:用大模型整理B站收藏夹与关注列表

你有没有过这种体验:B站关注列表已经涨到几百个UP主,刷首页像在随机抽卡;收藏夹还叫“默认收藏夹”,里面塞了三年都找不回来的教程;而粉丝数明明不低,却完全不知道他们想让你更新什么内容。

这其实是很多人忽略的一个事实:**账号积累了数据,不等于账号沉淀了资产。**关注、粉丝、收藏夹是B站用户最核心的三类“内容关系数据”,但默认产品形态里,它们几乎都处于不可管理状态。关注列表线性展示、收藏夹没有语义、粉丝列表没有标签。过去我们靠“手动整理文件夹Excel”的方式做,但面对几百上千条记录时,心智负担太重,整理成本远大于收益。

这才是我觉得“哔站理理”这类AI浏览器插件值得讨论的原因。它把B站个人数据当成一个可以被治理的“内容资产库”,在浏览器里既读数据,又用大模型做分类、打标签、分层,然后把整理建议交还给用户确认。这篇文章不打算只介绍一个产品概念,而是把它拆成开发者视角能落地的技术方案:浏览器插件怎么和B站页面共处、AI在什么环节介入、如何处理关注/粉丝/收藏夹三类数据、最小闭环怎么跑通、有哪些坑必须避开。如果你恰好是B站重度用户,或者对“浏览器插件 + AI Agent”组合感兴趣,这篇文章适合你收藏着慢慢读。

1. 为什么B站关注、粉丝、收藏夹会“失控”

1.1 三类数据的默认形态

先看清B站三类个人数据的真实属性。

关注列表本质是一张“单向订阅关系表”,产品上只给你线性的时间线。UP主数量一旦超过某个阈值,信息流就开始互相淹没。你关注了600个UP主,每天产生的更新视频可能有几百条,最后真正看完的只有十几条,剩下全靠“稍后再看”自我安慰。

收藏夹则更像一个“内容垃圾桶”。大多数人只会建“默认收藏夹”和“稍后再看”,很少维护分类。技术类收藏夹、美食教程、数码评测、知识区课程混在一起。一年之后打开,标题都认识,但“当时为什么收藏”这个上下文已经消失了。

粉丝列表问题更隐蔽。对个人创作者来说,粉丝不是数字游戏,而是内容运营的基础数据。普通创作者能看到的粉丝列表几乎不带标签,更没有任何群体画像。哪些人是真正会互动的活跃粉丝,哪些人来自某一条爆款视频的瞬时关注,哪些人其实早已不看B站,完全无从得知。

这三类数据的共同点是:**它们都存在,而且可视,但没有结构和语义。**技术上可以称之为“无Schema数据”。这也是为什么数据量越大,用户越容易焦虑。

1.2 “数据”不等于“资产”

数据要成为资产,至少要满足两个条件:可以被检索,可以被重新组织。

B站收藏夹提供了搜索框,但搜索只能解决“我记得标题关键词”的情况。更多时候你面对的问题是“我记得当时看过一个讲OpenCV调试的教程,但不知道哪个UP主,也忘了标题”——这种情况下搜索是无力的。

关注列表更麻烦。你想找出“最近三个月更新频率明显下降但内容质量仍然很高的UP主”,但B站没有提供按更新频率排序的视图。唯一可行的做法是把关注列表和每个UP主的主页信息拉出来做二次分析。这个系统能支撑前,浏览器插件就是最合适的载体。

当一个平台不能提供数据导出和自定义视图能力,而用户又确实持有这些数据的查看权时,“用户在页面上能看到的区域里,额外增加一层AI分析”就成了合理路线。这就是哔站理理这类浏览器插件存在的底层逻辑。

1.3 为什么现在被AI解锁

几年前就有人做过B站收藏夹整理工具,但普遍不好用。原因是传统规则式分类太僵硬。你想把“AI绘画”和“数字人”分开,传统代码要么写死规则,要么靠关键词命中,根本理解不了“Stable Diffusion工程化”应该归到哪个分组。

大模型的语义理解能力改变了这件事。你可以直接在Prompt里写:请把这些视频标题按主题聚成5到8个语义分组;你可以让模型根据UP主过往内容方向给关注列表做价值分层;你也可以让模型解释“这条收藏为什么值得去看”。

AI真正替代的不是“理解内容”这件事本身,而是把“需要逐条打开页面去理解内容”的劳动力剥削感消解掉了。AI浏览器插件则进一步把“读取页面数据—构建Prompt—调用大模型—展示整理结果”压缩成一次点击。

这是技术上完全可行的闭环,而且开发和部署成本低到个人开发者就能完成。下面开始拆结构和实现。

2. 哔站理理的核心架构与技术判断

先给一个关键判断:**想让AI整理自动化,不能只考虑大模型能不能分析,而要重新设计三层结构——数据怎么读、结果怎么算、动作怎么落。**这三层如果边界不清,插件很容易变成“能生成一堆建议却没法用”的玩具。

2.1 第一层:页面数据读取

浏览器插件读取B站数据通常有两种路径。

数据路径原理优点风险
DOM解析页面在B站页面上执行JavaScript,读取当前可见的卡片、标题、UP主名等不需要额外接口签名,权限可收敛到当前网站平台改版后选择器失效
抓取页面XHR接口通过开发者工具观察页面自身请求,模拟调用数据接口拿JSON数据结构稳定、字段全需要登录态、可能带签名参数,高频调用有风控风险

更稳妥的原则是:**优先读取用户当前页面可见的数据。**用户已经在B站页面上了,也已经登录了,插件不需要保存账号密码,更不需要做“扫码登录”。它只是替用户从页面上把数据“抄”下来,再交给AI。

“页面可见优先”还有一个隐藏好处:它天然控制请求频率。你只能读到用户滚到的内容,不可能一次把全站数据拖走,这让插件的风控风险大大降低。

2.2 第二层:智能分析由LLM承担

大模型在整理任务中扮演的是“结构化引擎”。

B站收藏夹的原始数据是一串视频标题,比如“用Python自动整理桌面文件”“iPhone 15详细评测”“机器学习入门课程推荐”。传统代码很难把这些归到“效率工具”“数码评测”“课程推荐”三个语义组;大模型可以做到,而且还能给你解释分组的理由。

这一层的核心不是换个更强的模型,而是把输出格式设计成可解析的JSON。如果你的Prompt只让大模型“自由发挥”,它确实可能给你一篇散文。正确做法是让模型严格按照你定义的Schema输出,只有输出可解析,后续动作才有可能落地。

2.3 第三层:动作执行必须留人工确认

整理动作包括改收藏夹名、把视频从一个收藏夹移动另一个、取消关注某个UP主、给某个UP主打上“核心内容源”标签。这一层最容易出事。

过去很多自动化脚本失败,不是技术问题,而是产品逻辑问题:用户内心并不想被AI“一键退坑”,他只想要“建议被充分理解后的一个确认动作”。

所以哔站理理的执行层应当做成“建议引擎 + 人工确认工作台”。AI给出建议,用户看一眼理由,再决定是否执行。“一键取消所有低活跃UP主”这种设计是灾难,不是效率。原因很简单:用户在关注某个UP主时,可能带着一段独特记忆,AI不知道,也不该替用户做最终决定。

读数据要克制,算数据要结构化,写数据要确认。这三层边界建立起来,插件工程才真正成熟。

3. 环境准备与项目初始化

3.1 开发准备

开发Chrome扩展不一定需要复杂的构建链。为了提高上手速度,可以完全不用打包工具,直接用原生JavaScript写,然后通过“加载已解压的扩展程序”方式调试。

推荐环境如下:

  • Chrome或Edge浏览器,使用Chromium内核即可。
  • Visual Studio Code作为编辑器,推荐安装“HTML CSS Support”和“JavaScript (ES6) code snippets”插件。
  • 不需要额外安装Node.js;但如果你希望写一些本地辅助脚本做离线分析,Node.js或Python都可以。
  • 一个可以访问的OpenAI兼容接口服务。建议选择国内合法提供的AI服务,只要支持/v1/chat/completions格式,就可以复用同一套代码。

3.2 建立工程目录

项目目录建议如下,名字先叫bili-organizer

bili-organizer/ ├─ manifest.json ├─ background.js ├─ content.js ├─ popup.html ├─ popup.js └─ assets/

这里没有用webpackvite等工具,并不是因为它们不好,而是对于一个以“读取页面DOM + 发请求 + 展示结果”为主的MV3插件,原生的manifest方案更直观,调试时也少一处“构建产物和源码不一致”的困惑。

3.3 大模型服务配置

由于文章示例需要接入大模型,这里有一个工程取舍:**浏览器插件属于客户端应用,直接在前端保存API Key并不是安全方案。**如果你做个人小工具,可以先把Key保存在chrome.storage.local里,图个方便;如果做生产级产品,强烈建议把大模型调用后置到自己的后端服务。

为了可复制、可演示,本文示例代码采用“轻量本地配置”方案:用户在自己的popup页面中填写API Base、API Key、模型名,插件把配置存到storage.local,再通过后台Service Worker发起请求。这种方式适合个人开发和学习,但不适合公开上线给大量用户使用,原因后面安全章节会展开说。

4. 第一个可跑通的最小闭环:收藏夹智能分类

不要一开始就做“关注列表全面管理”“粉丝资产分析”那种复杂功能。优先做收藏夹最小闭环:**打开收藏夹页面,读取十几条视频标题,让AI给出分组建议,然后展示给用户确认。**这个小闭环能验证架构是否跑通,也能让你体会AI整理和手工整理的真实差异。

4.1 页面数据收集

Content Script主要负责从收藏夹页面读取数据。B站的收藏夹页面是动态渲染的,浏览时你需要手动向下滚动,让“懒加载”把更多视频条目加载出来,再点击“整理本页收藏夹”。

这一步的采集逻辑通常包含:

  1. 判断当前页面是否包含收藏夹内容。
  2. 使用document.querySelectorAll找到视频卡片节点。
  3. 从节点中提取视频标题UP主名称
  4. 转成纯文本格式发送给后台。

这里必须坦白说:**B站页面改版比较频繁,代码里的选择器需要自己根据真实页面DOM调整。**下面会提供一套兼容性相对宽的写法和一段排查方式,但没办法保证永远不过期。

4.2 构造Prompt并调用大模型

收集到标题 + UP主列表之后,需要构造Prompt。Prompt写得好不好,直接影响输出质量。经验是先给System设定角色,再给User明确的数据和输出格式约束。

推荐Prompt模板如下:

你是B站个人内容整理助手。用户会提供一组收藏夹里的视频标题和UP主名称。 请先理解这些内容的主题,然后把它们聚合成5到8个语义分组。 每个分组必须包含: - 分组名称 - 一句话说明这个分组为什么成立 - 属于该分组的原始条目的序号列表 只输出JSON,不要输出Markdown,不要输出额外解释。 JSON格式必须是: { "summary": "对整个收藏夹的一句话总体判断", "groups": [ {"name": "分组名", "reason": "分组理由", "itemIndexes": [1,2,3]} ] }

在代码中调用大模型时,建议使用response_format: { "type": "json_object" }来约束输出。如果你的模型服务不支持这个参数,可以在System中强调“只输出JSON”,然后在解析失败时自动重试一次。

4.3 解析建议结果

大模型返回的文本应该是一段JSON字符串。解析后渲染到popup页面。用户在界面上看到“这个收藏夹里有Python自动化、设计素材、AI工具三个主题”,如果确认,插件可以把建议分组保存为一份快照,或者弹窗提示“建议你去收藏夹管理页,新建这几个收藏夹”。

这里不要图省事直接帮用户移动视频。B站收藏夹的视频移动属于修改型操作,一旦批量移动错了,整理成本比不整理还高。最小闭环的最优解是“给建议,让用户手动完成分组”,等用户信任结果后再逐步加入自动改名、批量移动等操作。

5. 核心功能二:关注列表的分层设计

5.1 从关注页取数

关注列表的取数逻辑和收藏夹类似,但页面结构不同。B站个人的“关注”页面一般可以滚动加载出所有已关注UP主。采集时需要读取的字段包括:

  • UP主昵称
  • UP主签名或简介
  • 是否已设置特别关注
  • 是否悄悄关注
  • 如果是B站创作者,最好能看到TA近期是否更新,这一信息能辅助判断活跃度

和收藏夹不同,关注列表的采集通常需要滚动多次才能完整加载。Content Script可以配合scroll事件或者定期执行window.scrollTo(0, document.body.scrollHeight)触发懒加载。更稳妥的办法是写一句提示:“请先手动滚动到页面底部,待所有UP主加载完成后再开始整理”。

5.2 分层建议:从“全部关注”到“内容矩阵”

关注列表分析的目标不是让用户清除关注,而是帮用户理解自己的关注结构。

大模型可以基于UP主昵称、个人简介和历史内容方向,把关注列表聚合成内容主题并标注活跃度。判断时我推荐用三种层级:

核心内容源:与用户当前兴趣或工作高度相关、更新稳定、值得每天看。这类UP主建议保持正常关注排列,以免被信息流淹没。

观察型内容源:内容有一定价值,但主题边缘、更新频率低,或者近期内容方向正在变化。这类可以放入“悄悄关注”或固定每周集中浏览一次。

可优化内容源:长期不更新、内容主题已经偏离兴趣、同质化严重。这些不一定直接取关,可以先移入“悄悄关注”观察两周再做决定。

这三个层级不是“AI替你退坑”,而是给用户一个信息流管理的操作框架。你可以在关注页面看到AI生成的分层结果后,自己做最终确认。

5.3 减少误操作:不要一键取关

关于关注列表,最重要的工程建议是:**不要写“一键取关”按钮。**至少不要默认开启。

即便AI判断某个UP主“三个月没更新”,也不排除用户和这位UP主有某种特定关系。批量取关的不可逆性很高,虽然B站允许重新关注,但看着一串“已关注UP主减少”的提示,用户心理体验并不好。更稳妥的产品逻辑是先把AI标记为“可优化”的UP主加入悄悄关注,给一个冷静期,之后再做二次确认。

执行时必须关注B站的频率限制,连续高频调用取关接口非常容易被判定为异常操作。间隔至少控制在数秒以上,并且每批操作后随机等待一段时间。

6. 完整参考代码与拆解

下面给出一个最小示例,不包含全部页面选择器适配,重点展示MV3插件的消息流:用户点击popup按钮,向content script发送采集请求,content script返回数据,popup再把数据组装成Prompt发给background,background调用大模型,最终渲染结果。

6.1 manifest.json

{ "manifest_version": 3, "name": "哔站理理", "version": "0.1.0", "description": "整理B站关注、粉丝、收藏夹的AI浏览器插件示例", "permissions": ["storage", "activeTab"], "host_permissions": [ "https://*.bilibili.com/*", "https://YOUR_LLM_DOMAIN.example.com/*" ], "action": { "default_popup": "popup.html", "default_title": "哔站理理" }, "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ["https://*.bilibili.com/*"], "js": ["content.js"], "run_at": "document_idle" } ] }

说明:

  • host_permissions中的B站域名用于content script注入。
  • YOUR_LLM_DOMAIN.example.com需要替换成你实际使用的大模型API域名。如果域名不在host_permissions里,后台发起fetch时可能遇到CORS或权限不足的报错。
  • MV3使用Service Worker作为后台,所有监听器必须是异步安全的,chrome.runtime.onMessage.addListener中如果用了sendResponse异步返回,必须return true

6.2 background.js

这个文件负责调用大模型。它接收popup发来的messagesconfig,然后向OpenAI兼容API发起请求。

// background.js async function askLLM(messages, config) { const apiBase = (config.apiBase || "").replace(/\/$/, ""); const apiKey = config.apiKey || ""; const model = config.model || ""; if (!apiBase || !apiKey || !model) { throw new Error("AI服务配置不完整,请先填写API Base、API Key和模型名"); } const resp = await fetch(`${apiBase}/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` }, body: JSON.stringify({ model: model, messages: messages, temperature: 0.2, max_tokens: 2048, response_format: { type: "json_object" } }) }); if (!resp.ok) { const errText = await resp.text().catch(() => ""); throw new Error(`LLM请求失败: ${resp.status} ${errText}`); } const data = await resp.json(); const content = data.choices?.[0]?.message?.content || ""; if (!content) { throw new Error("LLM返回内容为空"); } try { return JSON.parse(content); } catch (e) { // 如果模型没有严格遵守JSON输出,返回一个可追踪对象 return { raw: content, error: "JSON解析失败,模型返回的内容不是有效JSON" }; } } chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg?.type === "ORGANIZE_FAVORITES") { askLLM(msg.messages, msg.config || {}) .then((result) => sendResponse({ ok: true, result })) .catch((err) => sendResponse({ ok: false, error: String(err.message || err) })); return true; // 表示会异步调用sendResponse } });

这段代码中最容易踩坑的是response_format。如果你的模型服务基于某些开源模型,不支持json_object类型,调用会直接报400错误。此时可以把response_format去掉,并在Prompt里继续强调JSON格式,增加重试和字符串截取兜底。

6.3 content.js

Content Script只做一件事:采集当前页面的可见收藏夹条目。注意选择器需要根据真实DOM调整。

// content.js function collectFavorites() { // 这里的selector只作演示,B站改版后需要用开发者工具重新确认真实节点 const cards = document.querySelectorAll( ".fav-video-list .bili-video-card, [class*='fav'] [class*='video-card']" ); const results = []; for (const card of cards) { const titleNode = card.querySelector("a[title], .title, [title]"); const ownerNode = card.querySelector(".up-name, [class*='up-name'], [class*='author']"); let title = ""; if (titleNode) { title = titleNode.getAttribute("title") || titleNode.textContent || ""; } const owner = ownerNode ? ownerNode.textContent.trim() : ""; if (title) { results.push({ title: title.trim(), owner }); } // 最多采集30条,避免一次传给模型太多导致超时或结果不稳定 if (results.length >= 30) break; } return results; } chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg?.type === "COLLECT_FAVORITES") { const items = collectFavorites(); sendResponse({ ok: true, items, pageTitle: document.title }); } });

把采集数量限制在30条内,是刻意为之。一是控制单次请求长度,避免大模型处理过长文本后返回质量下降;二是减少对页面性能影响。如果你要处理几百条收藏,更合理的做法是分批处理。

6.4 popup.js

Popup是用户最直接的交互界面。它负责判断当前页面、发消息给content script、组装Prompt、调后台、渲染结果。

// popup.js async function getActiveBilibiliTab() { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab?.url?.includes("bilibili.com")) { throw new Error("请先打开B站收藏夹页面"); } return tab; } async function loadAIConfig() { const cfg = await chrome.storage.local.get(["apiBase", "apiKey", "model"]); return cfg; } function buildOrganizePrompt(rawText) { const system = ` 你是B站个人内容整理助手。 你会收到一个收藏夹内的视频标题和UP主名称列表。 请把内容聚合成5到8个语义分组。 只输出JSON,JSON格式为: { "summary": "总体判断", "groups": [ {"name": "分组名称", "reason": "分组理由", "itemIndexes": [1,2,3]} ] } `.trim(); const user = `请整理以下收藏夹条目:\n${rawText}`; return [ { role: "system", content: system }, { role: "user", content: user } ]; } function renderResult(result) { const output = document.getElementById("output"); if (!output) return; if (result?.error) { output.textContent = "模型返回内容无法解析:\n" + (result.raw || ""); return; } output.textContent = JSON.stringify(result, null, 2); } document.getElementById("organizeBtn")?.addEventListener("click", async () => { const status = document.getElementById("status"); const btn = document.getElementById("organizeBtn"); try { btn.disabled = true; status.textContent = "正在读取当前页面..."; const tab = await getActiveBilibiliTab(); const collectResp = await chrome.tabs.sendMessage(tab.id, { type: "COLLECT_FAVORITES" }); if (!collectResp?.ok || !collectResp.items?.length) { throw new Error("没有读取到收藏夹内容,请先滚动加载后再试"); } const rawText = collectResp.items .map((item, idx) => `${idx + 1}. ${item.title} | UP主:${item.owner}`) .join("\n"); status.textContent = "
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 14:00:46

BlenderMCP 完整指南:自然语言驱动 AI 3D 建模

BlenderMCP 完整指南:自然语言驱动 AI 3D 建模 【免费下载链接】blender-mcp Community plugin to control Blender 3D with any LLM of your choice 项目地址: https://gitcode.com/GitHub_Trending/bl/blender-mcp BlenderMCP 是一款通过 MCP(模…

作者头像 李华
网站建设 2026/9/4 13:59:33

[AutoSar]BSW_OS 04 Autosar OS_ISR 中断

目录关键词平台说明一、中断简介1.2 vector中断概述二 、中断向量表三 、中断分类3.1 开关中断控制3.2 二类中断3.2.1 配置3.2.2 code3.2.3 Nesting of Category 2 Interrupts3.3 一类中断3.3.1 一类中断的实现3.3.2 注意事项3.4 0类中断3.4.1 0类中断的实现3.4.2 注意事项关键…

作者头像 李华
网站建设 2026/9/4 13:58:05

别划走OBS预览:OBS Studio窗口置顶功能的完整上手指南

别划走OBS预览:OBS Studio窗口置顶功能的完整上手指南 【免费下载链接】obs-studio OBS Studio - Free and open source software for live streaming and screen recording 项目地址: https://gitcode.com/GitHub_Trending/ob/obs-studio 直播开着&#xff…

作者头像 李华
网站建设 2026/9/4 13:50:38

单量子比特也有指数级量子优势?信号学习新思路深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 13:49:45

LabVIEW与正运动控制卡:上位机开发实战指南

作为一个在工控圈摸爬滚打了十来年的老工程师,我这些年用过的上位机开发工具不少,从VB6到C#再到LabVIEW,都有涉猎。但说实话,如果纯粹从“快速上手”和“调试直观”这两个维度来看,LabVIEW在运动控制领域的优势依然是独…

作者头像 李华