夜雨聆风学习资料网

ARTICLE · 1010146

用 Chrome 插件采集网页数据:权限、脚本与导出

用 Chrome 插件采集网页数据:权限、脚本与导出

用 Chrome 插件采集网页数据:权限、脚本与导出

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

开头先问你一个问题

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

先把基础概念说清楚

AI 功能通常由输入、受控服务端调用、结构化结果、人工确认和可追踪记录组成。模型输出是候选信息,不是可以直接相信的最终事实。

Chrome 插件(Manifest V3)由三个角色组成:

角色
作用
脚手架里的对应文件
popup
点击图标弹出的窗口,放按钮、展示结果
pages/popup/src/Popup.tsx
content script
注入目标网页,直接读写页面 DOM
pages/content/src/index.ts
background worker
后台处理数据、跨页面通信
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

  1. pnpm dev
     启动后,项目根目录出现 dist/——这就是编译好的插件目录;
  2. Chrome 地址栏输入 chrome://extensions,打开右上角「开发者模式」;
  3. 点「加载已解压的扩展程序」,选择根目录下的 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
触发 CSV 文件下载
不需要给 content script 下载权限

*://*.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 ?? '',}));

三条边界:

  1. 找循环体
    :网页结构分析的核心是找到"循环反复的那个元素"——卡片找 article,表格找 tr,而不是复制整页;
  2. 避开 hash 类名
    :Coze 页面作者名类是 name--rm_hbL8bKLQSDcPE,这类 hash 是 CSS 构建时自动生成的,下次构建就变,必须让 AI 按元素位置规律写选择器(如 .semi-space 下的第 2 个 .semi-space 里的 span);
  3. 只采公开数据
    :不采集密码、私信、表单输入或超出用户预期的数据。

四、实战一:爬 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')

导出工具不是"只是文本",同样要把输入视为不可信。

八、发布前用三类页面测试

  1. 目标页面正常 → 能导出完整数据;
  2. 页面没有目标元素 → 显示「未找到可导出数据」,不要导出空 CSV 并假装成功;
  3. 页面 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 实践干货。

觉得有帮助的话,点个 「赞」 让更多人看到 👇

有技术问题欢迎留言交流,我们一起进步 💬

相关学习资料