浏览器插件实战:解锁复制限制与图片文字提取

作者:热心市民鹿先生2026.11.04 03:51浏览量:0

简介:针对网页禁用复制、图片取字需跨应用来回切换两大高频痛点,本文基于现代浏览器扩展规范,实战开发一款图文提取插件:用内容脚本在捕获阶段拦截站点的限制逻辑,恢复文本选中、复制与右键菜单;再串联可见区截图、框选裁剪与云端 OCR 接口,把取字流程收敛到侧边栏一站式完成,附关键代码与工程化踩坑经验,可直接照搬落地。

一、需求背景:两个被忽视的高频痛点

在日常的文字工作中,有两类操作出现频率极高,却始终没有被浏览器原生能力很好地覆盖。

痛点一:网页禁止复制。 不少设计灵感站、素材站、文库类站点出于内容保护的目的,会禁用文本选中、屏蔽右键菜单,甚至在 copy 事件里直接 preventDefault()。用户明明“看得见”文字,却“摸不着”,只能逐字手敲,效率极低。

痛点二:图片取字链路太长。 想从一张图里提取文字,常规做法是:截图 → 切到自带 OCR 能力的聊天或办公工具 → 发起识别 → 等待结果 → 复制 → 切回浏览器粘贴。一次取字要在多个应用之间往返三次以上,思路频繁被打断。

这两个痛点的共同本质是:操作流被割裂在不同工具之间,而浏览器本身完全有能力把它们收敛到一处。于是这个周末项目的目标就定下来了——做一个浏览器插件,把“解锁复制”和“图片取字”都装进浏览器里,所有操作在侧边栏完成,不遮挡页面、不切换窗口。

二、总体设计:一个扩展,两条能力线

插件基于当前主流的浏览器扩展规范(manifest.json 清单 + 多角色脚本)搭建,整体划分为四个角色:

  • content script(内容脚本):注入目标页面,负责“解除复制限制”与“框选区域”的页面内交互;
  • background(后台脚本,Service Worker):负责截图、调用 OCR 接口、权限管理等跨页面事务;
  • 侧边栏面板:插件常驻 UI,承载截图预览、识别结果、复制下载等交互;
  • storage(本地存储):按域名记忆解锁开关状态与用户偏好。

两条能力线相互独立又共享同一套 UI:能力一解决“选不中、复制不了”,能力二解决“图里的字取不出来”。

三、解锁复制限制:先懂原理,再谈反制

3.1 网站是怎么禁掉复制的

业界常见的限制手段主要有三类:

  1. JS 事件拦截:在 document 上监听 copy、cut、contextmenu、selectstart、dragstart 等事件并调用 preventDefault(),直接掐断浏览器默认行为;
  2. 内联事件属性:在 HTML 节点上写 oncopy="return false" 之类的属性;
  3. CSS 样式控制:通过 user-select: none 让文本根本无法被选中。

少数站点还会叠加“透明遮罩层盖住正文”或将正文放进嵌套 iframe 等进阶手段。

3.2 三层反制手段

第一层:在捕获阶段抢事件。 DOM 事件先经过捕获阶段、再进入目标与冒泡阶段。插件在 document 上以 capture: true 注册同名监听器并调用 stopPropagation(),事件就永远到不了站点自己的监听器手里:

  1. const TYPES = ['copy', 'cut', 'contextmenu', 'selectstart', 'dragstart', 'mousedown'];
  2. TYPES.forEach(type => {
  3. document.addEventListener(type, e => e.stopPropagation(), true);
  4. });

第二层:清空内联属性。 对挂在内联属性上的限制逻辑直接置空:

  1. ['oncopy', 'oncut', 'oncontextmenu', 'onselectstart', 'ondragstart']
  2. .forEach(attr => {
  3. document.documentElement[attr] = null;
  4. if (document.body) document.body[attr] = null;
  5. });

第三层:样式覆盖。 注入一段高优先级样式,把 user-select 强制改回可选中:

  1. * {
  2. user-select: text !important;
  3. -webkit-user-select: text !important;
  4. }

3.3 动态内容怎么办

大量站点的内容是异步渲染的,解锁逻辑必须能“追着 DOM 跑”。这里借助 MutationObserver 监听整棵 DOM 树,一旦有新增节点就对其重复执行解锁处理:

  1. const observer = new MutationObserver(() => unlock(document));
  2. observer.observe(document.documentElement, { childList: true, subtree: true });

3.4 工程上的分寸

解锁逻辑不能无脑全局常开:在线文档、富文本编辑器等场景本身就依赖这些事件的默认行为,强行解锁反而会破坏正常交互。因此插件把“解除限制”设计成按需开关:点击插件图标手动触发,状态按域名存入 storage,下次访问同站点自动生效,也可随时一键关闭。

四、图片取字:截图、裁剪与 OCR 的串联

4.1 四种识别入口

针对不同来源的图片,插件提供四条入口,覆盖绝大多数取字场景:

识别方式 操作说明 适用场景
整页截图 一键截取当前可见区域并识别 快速识别整屏文字
框选区域 在页面上拖拽出矩形范围 只取长页面中的局部文字
点击上传 从本地选择图片文件 识别已保存的图片
拖拽上传 将图片拖入侧边栏 零步骤快速识别

所有入口最终都汇入同一条流水线:取图 → 预览 → 送 OCR → 结果展示 → 一键复制/下载。

4.2 整页截图与框选裁剪

可见区截图调用扩展平台提供的截图接口(如 captureVisibleTab 一类的能力),拿到当前标签页的 DataURL。框选则由 content script 在页面里铺一层半透明遮罩,监听 mousedown / mousemove / mouseup 记录矩形坐标,再按坐标从截图上裁出目标区域:

  1. function crop(dataUrl, rect, dpr) {
  2. const img = new Image();
  3. img.src = dataUrl;
  4. return new Promise(resolve => {
  5. img.onload = () => {
  6. const canvas = document.createElement('canvas');
  7. canvas.width = rect.width * dpr;
  8. canvas.height = rect.height * dpr;
  9. canvas.getContext('2d').drawImage(
  10. img,
  11. rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr,
  12. 0, 0, canvas.width, canvas.height
  13. );
  14. resolve(canvas.toDataURL('image/png'));
  15. };
  16. });
  17. }

这里有一个极容易踩的坑:devicePixelRatio。截图的物理像素尺寸等于 CSS 尺寸乘以设备像素比,在高分屏上如果直接用 CSS 坐标裁剪,裁出来的区域会整体偏移。所以矩形坐标与裁剪尺寸都必须乘以 dpr 再处理。此外,按 Esc 要能随时取消选框;选框过程中页面若发生滚动,坐标应以视口为基准并刷新遮罩。

4.3 本地图片的两条路

  • 点击上传:侧边栏内放置 input[type=file],用 FileReader 读成 DataURL;
  • 拖拽上传:监听侧边栏的 dragover / drop 事件,从 DataTransfer 中取文件。顺带监听 paste 事件支持直接粘贴剪贴板截图,几乎零成本多覆盖一个高频场景。

4.4 接入云端 OCR

识别能力接入云端 OCR 服务(例如百度智能云的文字识别服务)。这类服务的调用流程高度一致:鉴权 → 传图(Base64 或图片地址)→ 返回结构化 JSON → 按行拼接文本。主流云厂商的 OCR 服务普遍提供一定的免费调用额度,个人日常使用完全够用。

  1. async function recognize(imageBase64) {
  2. const resp = await fetch(OCR_API, {
  3. method: 'POST',
  4. headers: { 'Content-Type': 'application/json' },
  5. body: JSON.stringify({ image: imageBase64 })
  6. });
  7. const data = await resp.json(); // 字段名以所选 OCR 服务的文档为准
  8. return data.wordsResult.map(w => w.words).join('\n');
  9. }

识别结果在侧边栏以可编辑文本呈现:先手动修掉个别错字,再一键复制或下载为 txt,形成完整闭环。

4.5 为什么放侧边栏

侧边栏是体验上的关键决策:

  • 不遮挡页面:识别期间原页面保持可见,方便对照上下文;
  • 常驻不消失:不像弹出面板那样点别处就收起,适合“边看边取字”的连续操作;
  • 交互集中:预览、结果、复制、下载在同一块区域完成,工作流不被打断。

五、工程细节与踩坑记录

  1. 权限最小化:能申请 activeTab 就不要申请全站 host 权限。activeTab 只在用户主动触发插件时授予当前标签页的临时权限,截图与脚本注入都够用,用户也更放心。
  2. 密钥安全:OCR 密钥绝不能硬编码进扩展包——扩展代码在本地是可查的。稳妥做法是自建一层极简转发服务,扩展只请求自己的服务端,密钥留在服务端;即使纯自用,也不要把含密钥的代码提交到公开仓库。
  3. 识别质量优化:小字、低对比度场景识别率会下降。实用技巧是框选时多框一些上下文、必要时把图片等比放大一倍再送识别、JPEG 压缩质量控制在 0.9 左右,兼顾清晰度与传输体积。
  4. iframe 与动态页面:对嵌套 iframe 的站点,需在清单中声明 all_frames 才能把内容脚本注入进去;SPA 路由切换后要重新执行解锁逻辑,避免“首页能用、翻页失效”。

六、效果与延伸方向

实际使用反馈很直接:禁复制的站点一键恢复选中与右键,复制如常;取字从“截图—切应用—识别—复制—切回”五步压缩为“点一下、框一下”两步,全程不离开浏览器。

在这个骨架上还有许多低成本的延伸:识别结果即时翻译、连续截图的历史记录、多图批量识别、全局快捷键触发、识别文本直接追加到笔记等。本质上,浏览器扩展最大的价值在于把散落各处的碎片化操作,收敛成一条不被打断的工作流——这也正是这类小工具最值得亲手实现的原因。