原文

项目中需要从百度图片和谷歌图片批量抓取一系列关键词的图片,而且需要是大图资源,不能是缩略图。在后端通过http请求直接拉取内容抓取,遇到下面两个问题:

有的大图地址是在前端通过脚本生成的,拉取页面内容之后无法直接得到大图地址
翻页请求并不是简单的pageindex++,拿到下一页内容。抓取第一页后边的内容也需要分析翻页请求链接组装,以及返回的数据如何解析。
这两个问题导致通过后端爬取大图列表十分困难。于是我想起了以前玩过的杂技——浏览器插件。通过javascript控制浏览器打开网页,搜索关键词,页面渲染完毕之后拿到大图地址,第一页拿完之后让页面滚动到底部,继续加载图片,and so on!直到拿到足够数量的图片。做完这个小工具,想着总结一下经验,加深点印象,免得以后某一天有需要再来做的时候一脸懵逼,于是抽时间慢慢写下这边文章记录一下我对浏览器插件的认识。

什么是chrome浏览器插件
地址栏右侧那些icon就是一个个浏览器插件。点击插件图标可以弹出插件窗口。我所理解的chrome浏览器插件功能有三大块:

弹出一个窗口,让用户执行操作,或者显示信息
向网页中注入脚本文件,执行某些功能
调用chrome提供的native api,执行浏览器tab页开关、窗口开关、文件下载等操作
如上图腾讯电脑管家的插件,就是用来提供广告过滤功能的。它的工作原理应该就是给需要过滤的网址插入一段脚本,来把页面上的广告标签干掉。

chrome插件开发
文档地址:https://developer.chrome.com/extensions/overview

代码模块
chrome插件完全由javascript、html、css开发,和上面的插件功能相对应,代码也可以分为三大模块:

popup 弹出窗口代码集合。弹窗UI通过html+css开发,弹窗中也可以引用js脚本来控制交互操作。每个弹窗都相当于一个独立的tab页,运行在其中的js脚本拥有一个独立的上下文。
inject.js。注入网页文件的脚本。需要注意的是,注入的脚本上下文也是独立的,它可以操作目标网页DOM,但是并不在目标网页脚本的上下文中。
background.js。插件后台脚本,拥有独立的上下文,且此上下文是唯一的,无论浏览器打开多少个tab页,background.js的上下文都不会变化,除非关闭浏览器。
这三块代码之间的关系我画了个图方便理解:

图中,黑色的部分代表chrome原生部分,其他的每一个方块都拥有一个独立的javascript上下文。

manifest.json
chrome插件有一个比较重要的配置文件,manifest.json,用来指定各个模块的代码文件名、插件权限、插件图标、inject脚本插入时机等

{"name": "imagefetcher","version": "0.0.1","manifest_version": 2,"description": "抓取图片网站大图文件","background": { "scripts": ["dist/background.js"] },"icons": { "16": "icon.jpg","48": "icon.jpg","128": "icon.jpg" },"permissions": ["tabs","downloads","http://*.baidu.com/","http://*.google.com.hk/"],"browser_action": {"default_icon": "icon.jpg" ,"default_title": "抓取图片","default_popup": "index.html"},"content_scripts":[{"run_at":"document_end","matches":["<all_urls>"],"js":["lib/jquery-2.0.0.min.js", "dist/inject.js"]}]
}

background指定background代码文件路径;content_scripts指定inject的脚本列表,以及注入的条件、注入时机;browser_action中的default_popup指定popup弹出的html文件路径;permissions指定能访问的网页或chrome提供的一些功能的权限;icons指定插件图标。

模块间通信
按照上面的结构,很容易可以联想到各个模块的分工:popup模块 的代码负责显示弹窗,让用户输入关键词,下发开始抓取指令;显示抓取进度;下发下载指令。inject.js负责分析网页的DOM,拿到大图资源链接,并翻页,直到获取足够数量的图片。background.js负责汇总各个网页抓取的结果,并将结果显示到弹窗中。由于这些脚本拥有各自的执行上下文,并不能通过直接调用函数的方式来通信,所以我们需要通过chrome提供的方式来进行模块间的通信。

与background的通信
在popup中或者inject中发出消息给background接收。

chrome.runtime.sendMessage({action:ACTION.START_FETCH,data:xxxx});

sendMessage函数还可以接受一个回调函数,处理收到消息之后处理的返回结果。 rome.runtime.sendMessage(string extensionId, any message, object options, function responseCallback)

在background.js中监听消息:

chrome.runtime.onMessage.addListener(function(request, sender, sendRequest){var data=request.data;var fetch;if(data.tab_id){fetch=window.FETCH_ITEMS.getByTabId(data.tab_id);}//开始抓取消息,读取抓取队列的第一个,开始抓取,抓取完成之后继续读if(request.action==ACTION.START_FETCH){__fetch_list.push(data);readFetchList();}else if(request.action==ACTION.FETCH_PROGTRESS){fetch.urls=data.urls;}//抓取完成,修改fetch_item状态,若存在弹窗,通知弹窗刷新视图else if(request.action==ACTION.FETCH_SUCCESS){fetch.status=DOWNLOAD_STATUS.SUCCESS;fetch.urls=data.urls;}
});

与inject的通信
要指挥inject的脚本执行一些操作,必须给inject发消息,而inject是注入网页中的,所以发消息第一步必须先获取tab页的tabid,然后将消息发给特定的tab页。在background中发出消息:

chrome.tabs.getSelected(function(tab){chrome.tabs.sendMessage(tab.id, data, function(response) {console.log(response);});
});

inject中接收消息:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){var data=request.data;//开始抓取if(request.action==ACTION.START_FETCH){var site=SITES.getSite(data.site);if(!site){sendResponse({err:1,message:"未实现此网页抓取"});return;}sendResponse({err:0});fetcher=require('./fetchors/'+data.site);fetcher(data).then(function(urls){data.urls=urls;chrome.runtime.sendMessage({action:ACTION.FETCH_SUCCESS, data:data}); //发送给background}).done()}
});

调试
不能调试还写什么代码! chrome插件的三大模块也是可以调试的,只不过都藏在各种犄角旮旯里边,下面扒一扒怎么分别给他们打断点。

background
打开chrome://extensions/点“检查视图”后边的链接,就可以打开控制台了,在source里边打上断点,调试走起

popup
在插件图标上右键——审查弹出内容,打开控制台,在source里边打上断点,调试走起

inject
按F12打开被注入的页面的控制台,点Sources,点右侧中间的Content Scripts,就可以看到这个页面被那些插件注入了脚本了,根据名称找到自己的脚本,打上断点,调试走起

使用vue开发chrome插件
vue带来的好处
干掉DOM操作
我开发的chrome插件是一个用来完成图片下载任务的插件。抓取过程中,需要显示抓取进度,并可以进行删除下载,其中涉及很多DOM操作。使用vue可以减少大量的dom操作代码,这个不细讲,参见http://vuejs.org/guide/

干掉复杂的通信
这个是我觉得用vue开发chrome插件最有价值的部分了,前边介绍了插件几大模块之间的通信,需要调用chrome提供的接口进行频繁的发送消息和监听处理。通过

chrome.extension.getBackgroundPage()

可以拿到插件background脚本的window对象,注意这个background的执行上下文是只有一个的,所以在插件运行期间我们可以用它来存储各个tab页抓取回来的数据。在popup的脚本中:

var FETCH_ITEMS=chrome.extension.getBackgroundPage().FETCH_ITEMS;
var vm=new Vue({el: '#wrapper',data: {FETCH_ITEMS: FETCH_ITEMS},methods:{}
});

这样,将从各个tab页抓取回来的数据push到background.js暴露出来的一个对象中,popup弹出的网页中就可以实时显示抓取进度了,不需要在popup和background之间编写大量的通信代码。至于各个tab页和background之间的通信,可以使用上面chrome提供的通信方式,也可以自己拿到background的window对象暴露出的变量,再进行操作。这里并没有复杂的视图更新和用户操作,所以怎么通信都无所谓了~

代码结构
按照上面理解的结构,每个模块的代码集中到一起。另外,插件中也允许根据路径直接访问插件中的资源,其路径是“chrome-extension://[extensionId]/[resourceName]” 。select目录中存放的是一个用来筛选图片的页面代码,在popup页面中直接跳转到/select.html即可打开此页面。

插件开发完成打包之后,把这些资源放到一个文件夹中然后打开chrome://extensions/选择上面的目录即可看到地址栏右侧出现插件的图标。


初次在chrome插件开发中使用vue的时候,遇到了这样一个问题:模型更新了,视图始终不更新,然后打开调试界面后运行vm.$mount("#wrapper");,视图却更新了。百思不得其解,google之,发现chrome插件中有些javascript代码写法和正常环境中有所不同,幸好vue居然贴心的为这种情况准备了一个特殊的包,不然就前功尽弃了 :(这里总结了一个经验,用前端框架开发的过程中最好不要使用.min的包,打包好的代码里边会去掉一些警告的逻辑… 把.min包替换成非压缩包之后,看到了这个错误:根据提示,在vue的项目里边找到了一个叫CSP的分支https://github.com/vuejs/vue/tree/csp/dist看文档说明,果然就是为插件开发定制的啊! 没想到那么偏门的场合他们也有关注到!

使用vue 开发chrome 插件相关推荐

  1. 一文教会你如何用Vue开发Chrome插件

    前言 作为一个常年的B端前端开发者来说,千篇一律的业务开发有着些许的枯燥无味.在联调过程中,会经常发现后端在部署服务,然后又不知什么时候部署好,由于公司的部署系统查看系统部署状态入口较深,所以闲暇之余 ...

  2. 记录--使用Vue开发Chrome插件

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 环境搭建 Vue Web-Extension - A Web-Extension preset for VueJS (vue-web-ex ...

  3. 利用Vue 脚手架 开发chrome 插件,太方便了

    前言 一个 Chrome 插件,核心就是 manifest.json 文件,manifest.json 下的属性 content_scripts 指定inject的脚本列表 js,css [ inje ...

  4. 使用Vue3+Element Plus开发Chrome插件

    使用Vue3+Element Plus开发Chrome插件 引言 初始Vue项目的创建 Element-Plus组件的安装及导入 其他文件配置 .eslintrc.js文件 vue.config.js ...

  5. 开发chrome 插件, background.js中 console log 看不到解决方法

    开发chrome 插件, background.js中 console log 看不到解决方法 参考文章: (1)开发chrome 插件, background.js中 console log 看不到 ...

  6. blazor wasm开发chrome插件

    用blazor(Wasm)开发了一个chrome插件感觉效率挺高的,分享给大家 先简单介绍下WebAssembly的原理: "WebAssembly是一种用于基于堆栈的虚拟机的二进制指令格式 ...

  7. 使用 React.js 开发 Chrome 插件

    (点击上方公众号,可快速关注) 来源:UncleChen unclechen.github.io/2017/06/16/使用ReactJS开发Chrome插件/ 一.背景 相信看到这篇文章的人应该都用 ...

  8. React开发chrome插件系列教程之chrome插件基本介绍

    文章目录 chrome插件能干什么 chrome插件的版本 chrome插件的浏览器支持 chrome插件的功能组成 manifest.json popup content script backgr ...

  9. 开发chrome插件(扩展)

    官方文档 https://developer.chrome.com/extensions/getstarted.html [干货]Chrome插件(扩展)开发全攻略 http://blog.haoji ...

  10. 试着开发chrome插件

    我的第一个chrome插件,是app形式的 代码如下 创建一个文件: 1.manifest.json 1 { 2 "version": "1.0", 3 &qu ...

最新文章

  1. 变态最大值--nyoj题目811
  2. 廖雪峰历时 3 个月打磨出价值 1980 的数据分析教程,终终终于免费啦!
  3. Java黑皮书课后题第6章:**6.29(双素数)双素数是指一对差值为2的素数。例如,3和5就是一对双素数,5和7是一对双素数,而11和13也是一对双素数。编写程序,找出小于1000的所有双素数
  4. 20155327第三周学习总结
  5. Cortex‐M3-存储器保护单元(MPU)
  6. MySQL中查询获取每个班级成绩前三名的学生信息
  7. 用计算机打出二分之一,win10手机计算器怎么输入二分之一?
  8. 《Python Cookbook 3rd》笔记(5.5):文件不存在才能写入
  9. Winform中ComcoBox控件设置选定项
  10. java语言乘号_java的编码样式、运算符运算对象
  11. Ext Store Proxy Ajax
  12. matlab怎样定义全局变量,Matlab如何定义公共变量
  13. vnc远程,在windows下如何实现vnc远程
  14. 不联网服务器系统时间,电脑时间不准确联网自动调整步骤
  15. 你到底是前端人还是搬砖人?推荐一款国产摸鱼神器!
  16. 高清渐变动态壁纸来了 手把手教你做动态壁纸
  17. 计算机应用基础——计算机硬件(二)
  18. STM32CubeMx-SPI读写W25QXXX
  19. GO语言基础----简易计算器
  20. 【小程序源码】2022强大的修复版趣味心理测试小程序

热门文章

  1. Python工作任务自动化教程
  2. 证明一维热传导方程为抛物型方程
  3. Spring框架爆RCE 0day漏洞的临时解决方案
  4. win7备份工具_调解 win7系统一键还原精灵使用的具体方法 -win7系统使用教程
  5. java xml解析 jdom_Java语言中XML的JDom解析方式
  6. 苹果电脑连接打印机操作
  7. 台达plc自由口通讯_台达PLC的通信协议??
  8. ARM-linux开发板网线连接电脑访问外网
  9. ChromeDriver的浏览器版本不支持解决办法
  10. 银联网关支付 java版