ARTICLE · 1010146
用 Chrome 插件采集网页数据:权限、脚本与导出
用 Chrome 插件采集网页数据:权限、脚本与导出
浏览器插件能把网页中的表格、标题和链接导出,但它运行在用户浏览器里。权限申请、数据范围和导出格式必须在第一版就设计清楚。本文把「开发 Chrome 插件:把上班刷知乎变成看飞书文档」和「用 Chrome 插件爬取一切网页数据」两套课程实战合并成一条可运行主线:从脚手架、开发模式加载,到爬 Coze 模板卡片、新浪财经表格并导出 CSV,每一步都给出可复制命令、可执行提示词和可对照的验证结果。

开头先问你一个问题
AI 功能最容易做成一段惊艳演示,也最容易在真实用户、成本和权限面前失效。先看清这条功能链路,再开始接模型。

先把基础概念说清楚
AI 功能通常由输入、受控服务端调用、结构化结果、人工确认和可追踪记录组成。模型输出是候选信息,不是可以直接相信的最终事实。
Chrome 插件(Manifest V3)由三个角色组成:
pages/popup/src/Popup.tsx | ||
pages/content/src/index.ts | ||
chrome-extension/src/background |
本主线要完成的采集链路:
用户在目标网页 → 点 popup 按钮 → content script 读取 DOM(或自动翻页)→ 组装字段数组 → 转 CSV → 本地下载 → Excel/WPS 打开核对
关键点:插件运行在用户浏览器里,页面 DOM 天天变、网站随时改版。选择器必须先在控制台测过、能快速替换;权限必须最小。
一、搭骨架:环境搭建与开发模式加载
1.1 下载脚手架
GitHub 搜索 chrome-extension-boilerplate,课程实测推荐 chrome-extension-boilerplate-react-vite(pnpm 管理依赖、Vite 构建、React 写 popup):点 Code → Download ZIP,解压后用 Cursor 打开。
1.2 安装依赖并启动
npm install -g pnpm@latest-10 # 未装 pnpm 时执行;版本要求以当前官方文档为准pnpm install # 安装项目依赖pnpm dev # 开发模式构建,终端提示本地服务启动(默认 8081 端口)
Windows 用户需先启用 WSL 并安装 Linux 发行版,开发构建建议以管理员身份运行。
1.3 加载到 Chrome
pnpm dev启动后,项目根目录出现 dist/——这就是编译好的插件目录;Chrome 地址栏输入 chrome://extensions,打开右上角「开发者模式」;点「加载已解压的扩展程序」,选择根目录下的 dist文件夹,固定到工具栏。
加载后先关掉两个默认功能,否则会出现"新标签页被插件覆盖"和"页面底部多一行注入元素"两个怪现象:
// chrome-extension/manifest.ts(生成 manifest.json 的源文件)// 删除 newtab 覆盖:chrome_url_overrides: { newtab: 'new-tab/index.html' },// 删除 content-ui 注入:{ matches: ['http://*/*','https://*/*','<all_urls>'],// js: ['content-ui/index.iife.js'] },
⚠️ 修改 manifest 后必须回到
chrome://extensions点刷新按钮才生效,再刷新目标网页重测。
二、manifest 权限:只申请必需项
{ "manifest_version": 3, "name": "小狗爱摸鱼", "permissions": [ "activeTab", "scripting", "downloads" ], "host_permissions": [ "https://www.coze.cn/*", "https://vip.stock.finance.sina.com.cn/*" ], "content_scripts": [ { "matches": [ "*://*.zhihu.com/question/*" ], "js": [ "content/index.iife.js" ] }, { "matches": [ "*://*.zhihu.com/question/*" ], "css": [ "content.css" ] } ]}
activeTab | <all_urls> 克制得多,优先用它 | |
scripting | ||
downloads |
*://*.zhihu.com/question/* 用通配符匹配详情页:问题 ID(如 /question/390635068 末尾的数字)会变化,通配符保证不受影响。不要为了省事申请 <all_urls> 全站访问权。
三、内容脚本:只提取公开、必要的数据
content script 直接操作页面 DOM。课程里「复制知乎所有答案」的写法:在 pages/content/src/index.ts 加悬浮按钮,点击后把页面所有 .AnswerItem 元素的文本复制到剪贴板,再丢给 DeepSeek 总结。提取数据的最小模板:
const rows = [...document.querySelectorAll('.card')].map((card) => ({ title: card.querySelector('.title')?.textContent?.trim() ?? '', url: card.querySelector('a')?.href ?? '',}));
三条边界:
- 找循环体
:网页结构分析的核心是找到"循环反复的那个元素"——卡片找 article,表格找tr,而不是复制整页; - 避开 hash 类名
:Coze 页面作者名类是 name--rm_hbL8bKLQSDcPE,这类 hash 是 CSS 构建时自动生成的,下次构建就变,必须让 AI 按元素位置规律写选择器(如.semi-space下的第 2 个.semi-space里的 span); - 只采公开数据
:不采集密码、私信、表单输入或超出用户预期的数据。
四、实战一:爬 Coze 模板卡片,导出 CSV
目标:https://www.coze.cn/template。固定三步法:分析结构 → 写选择器 → 测试。
4.1 分析网页结构
右键目标卡片 →「检查」→ 元素选择器高亮,确认第 1、2、3… 张卡片是同一个循环体 → 右键 →「以 HTML 格式修改」→ 复制整段卡片 HTML(一张即可)。
4.2 Ask 模式让 AI 写选择器
把卡片 HTML 粘贴给 Cursor(Ask 模式,建议 Claude 3.7 模型):
请你给到我获取这个页面上所有类似元素的选择器 javascript 代码,并且返回给我数组的形式。我希望能够爬取所有数据,我要爬取的字段包括:1. 背景图: bgImg 2. 应用类型: appType 3. 标题: title4. 作者: author 5. 描述: desc 6. 价格: price 7. 复制次数: copyCount需要注意:不要使用特殊类名或者包含 hash 的元素选择器,而是应该找元素位置的规律
4.3 控制台测试(验证点 1)
把 AI 返回的代码粘贴到目标网页的控制台执行:
function scrapeCozeTemplates() { const templateCards = document.querySelectorAll('article.flex'); return Array.from(templateCards).map((card) => ({ bgImg: card.querySelector('.semi-image img')?.getAttribute('src') ?? '', // 其余字段同理:每个字段一个选择器,取 textContent.trim() }));}scrapeCozeTemplates();
素材里真实出现的失败分支:字段大多正确,但 price 抓成了卡片上的「智能体」标签,而不是底部的「免费」行。修复:把错误描述回喂给 AI——"其他字段都对,但价格抓错了,抓成了智能体标签,应该是免费那一行,请修改 price 的选择器"——重新生成后在控制台重测。注意数组索引从 0 开始:第 5 张卡片是 templates[4],它的价格应为 "9.9"(元)。
4.4 Agent 模式接入 popup
切换到 Agent 模式,把测试通过的代码贴进去,再追加:
请你根据上面的代码,完成数据获取逻辑:1. 移除掉 @Popup.tsx 中所有的样式2. 在 @Popup.tsx 中添加一个按钮,按钮叫做:获取coze模板数据3. 该按钮只有在 https://www.coze.cn/template 页面才能点击4. 用户点击按钮,执行你上面的代码,获取数据;数据保存为 csv 格式并本地下载
修改后回目标页测试:点「获取coze模板数据」→ 浏览器自动下载 CSV → Excel/WPS 打开核对 7 个字段。
4.5 数据清洗(可选优化)
可让 AI 加在导出前:价格是「免费」→ 显示 0 元;带人民币符号只留数字;复制次数带大 K(如 1.2K)→ 换算成真实数字(×1000)。改完记得 git commit——课程习惯是每完成一个功能提交一次。
五、实战二:爬新浪财经表格 + 自动翻页
目标:https://vip.stock.finance.sina.com.cn/mkt/#china_us 的中概股数据,共 8 页。表格爬取和卡片不同,三个坑要提前避开。
5.1 坑 1:页面上有 7 个 table
控制台运行 document.querySelectorAll('table') 返回 7 个元素,目标表格是最后一个,直接选 table 会抓错。要找上级父元素里特征明显的锚点:目标表格的父容器有唯一 ID #tbl_wrap。选择器优先级:有唯一 ID 用 ID,没有 ID 才用 class。
5.2 坑 2:整段 HTML 太长,AI 处理不了
表格结构有一千多行,直接粘贴会超出 AI 上下文。做法:项目里新建 temp.html(输入 ! 回车生成 HTML 骨架),粘贴整段表格并格式化,删除 tbody 下多余的 <tr> 只保留 1~2 行,再 Ask 模式:
@temp.html 请你给到我一个选择器的脚本,用于获取该页面中类似表格的所有数据(行数不固定)。注意需要在选择器的代码中添加上 tbl_wrap 这个 id 的选择
AI 给出的核心代码:
function extractTableData() { const table = document.querySelector('#tbl_wrap table'); if (!table) { console.error('Table not found'); return null; } // 表头:thead tr 里 th/td 取 a 标签文本;行:tbody tr 逐行,前两列取 a 文本 return { headers, rows };}const tableData = extractTableData();console.log(tableData); // 验证点 2:headers 完整、每行字段与表头一一对应
5.3 坑 3:只抓第一页
单页验证通过后,在 Agent 模式把分页器 HTML 给 AI(分页器在 #list_nav2,当前页是 <span class="pagecurr pagedisabled">8</span>;到最后一页时「下一页」按钮会出现 pagedisabled 类名):
这个页面有一个分页的逻辑,我希望我在点击按钮后,你能自动帮我从第一页自动的开始跳转,等 2 秒后获取当前页面数据,然后跳转下一页,直到到最后一页。到最后一页的标志是下一页按钮出现 pagedisabled 这个类名。然后保存全部的数据到我的 csv 数据表中
素材里真实出现的失败分支:第一版翻页逻辑只抓到了第一页 80 条。修复:给 AI 描述 bug——"现在有 Bug,你还是只获取到了第一页的数据,没有获取到后面页面的数据,请你修改这个 Bug"——并附上分页器 HTML。等 2 秒是因为翻页是网络请求,要等新一页数据从后端加载回来再抓,否则抓到的是当前页的旧数据。验证点 3:回到第一页重新点按钮,自动翻完 8 页后下载 CSV,应为 8 × 80 = 640 条数据。
六、popup 与消息传递:校验来源和结构
popup 与 content script 通过消息通信。即使是自己的插件,也不要把任意 message 当可信命令:
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message?.type !== 'extractRows') return; // 只认自己的消息类型 if (!Array.isArray(message.rows)) return; // 校验数据结构 sendResponse({ count: message.rows.length }); return true; // 异步回复必须 return true,否则端口提前关闭,回调不生效});
content script 只在目标页面读取 DOM,popup 不要直接假定页面 DOM 存在;异步回调漏掉 return true 会报 "The message port closed before a response was received"。
七、导出前预览与 CSV 防公式注入
导出前让用户看到条数和前几行,再下载。CSV 有两个坑:引号转义和公式注入。用户可控文本以 =、+、-、@ 开头时,表格软件可能把它当公式执行:
function safeCell(value) { const text = String(value ?? ''); const escaped = text.replaceAll('"', '""'); return /^[=+\-@]/.test(escaped) ? `"'${escaped}"` : `"${escaped}"`;}// 组装:['title,url', ...rows.map((x) => `${safeCell(x.title)},${safeCell(x.url)}`)].join('\n')
导出工具不是"只是文本",同样要把输入视为不可信。
八、发布前用三类页面测试
目标页面正常 → 能导出完整数据; 页面没有目标元素 → 显示「未找到可导出数据」,不要导出空 CSV 并假装成功; 页面 DOM 结构变化 → 提示插件版本可能不兼容,需要更新选择器。
爬虫类插件的额外边界:只申请最小权限;不采集密码、私信、表单内容;目标网站是否允许此类自动化采集,需自行确认(遵守网站条款与 robots 约定);导出数据仅用于个人学习分析。上架 Chrome 应用商店需注册开发者账号并缴纳一次性费用,费用与审核要求以当前官方文档为准。
深度实战:26 Chrome插件采集网页数据 权限脚本和导出
概念与边界
Manifest V3 由 popup、background worker、content script 与最小权限组成;content script 只读公开 DOM,不绕过登录、不采敏感数据;选择器必须先在控制台测过、可快速替换。
从零实现
chrome-extension-boilerplate-react-vite 脚手架 + pnpm install/pnpm dev 产出 dist/,开发模式加载;manifest 只申请 activeTab/scripting/downloads;content script 按字段数组取数据,popup 按钮触发,CSV 本地导出并做公式注入净化;matches 通配符限制只在目标域名显示。
失败分支与处理
hash 类名失效→按元素位置规律重写;多个 table 抓错→用唯一 ID(#tbl_wrap)锚定;HTML 太长超上下文→temp.html 删到 1~2 行循环体;price 抓错→把错误样例回喂 AI 改选择器;翻页只抓第一页→等 2 秒再抓 + pagedisabled 判末页;newtab 被覆盖、content-ui 多余元素→manifest 删除后到扩展页刷新。
可验证验收
控制台运行选择器函数返回数组且 7 字段正确(templates[4].price === "9.9");document.querySelectorAll('table') 为 7、#tbl_wrap 唯一;翻页后 CSV 恰好 8×80=640 行;免费→0 元、1.2K→1200 清洗生效;权限仅三项且无 cookie/localStorage。
发布前检查清单
[ ] pnpm dev 后 dist 目录存在,chrome://extensions 加载成功、已固定到工具栏[ ] manifest 权限仅 activeTab/scripting/downloads(按需最小化),无 <all_urls>[ ] 插件只在目标域名显示(matches 通配符正确),newtab 覆盖与 content-ui 已移除[ ] 选择器避开 hash 类名,控制台实测过每个字段[ ] 无目标元素时提示「未找到可导出数据」,不导出空 CSV[ ] CSV 已做引号转义与公式注入处理(= + - @ 前缀转义)[ ] 不采集密码、私信、表单输入,导出前用户可预览条数与前十行[ ] 目标网站是否允许自动化采集已自行确认[ ] 单页导出与 640 条全量翻页均已实测[ ] npm run build 产物体积明显小于 dev 包,可直接分享或上架
关注 「斌哥聊技术」,分享更多 AI 实践干货。
觉得有帮助的话,点个 「赞」 让更多人看到 👇
有技术问题欢迎留言交流,我们一起进步 💬