• 浏览器插件开发爬虫记录


    常用爬虫有各种各样的反爬限制,而如果是小数据量并且该网站反爬手段非常厉害的前提下,可以考虑使用浏览器插件作为爬虫手段

    基本代码来源于这位博主分享的插件代码, 主要在他的基础上加了 请求代理、管理面板、脚本注入拦截到的请求数据和管理面板之间的交互

    基本项目结构如下:
    在这里插入图片描述

    1 manifest.json

    manifest.json 是浏览器加载插件、插件权限申请的配置文件

    {
      "name": "xx爬虫",    // 插件名称
      "version": "1.0",
      "manifest_version": 2,
      "browser_action": {
        "default_popup": "/html/popup/popup.html"    // 在浏览器上点击插件展示的页面内容
      },
      "permissions": [     // 权限申请,目前申请了代理、tab、存储等功能
        "proxy",
        "tabs",
        "storage",
        "notifications"
      ],
      "externally_connectable": {
      "matches": ["*://*.example.com/*"]
    },
      "content_scripts": [    // 网页js注入管理程序
        {
          "matches": ["*://xxx.com/*"],
          "js": ["/js/content/page.js", "/js/content/install.js"],   // 注入哪些js
          "run_at": "document_start"    // 在哪个时间点注入
        }
      ],
      "web_accessible_resources": [
        "/js/inject/pikazExcel.js",
        "/js/inject/page.js",
        "/js/inject/network.js"
      ],
        "background": {    // 浏览器启动的时候,插件就在后台运行的js,是一个全局生命周期的存在
        "scripts": ["/js/background/background.js"],
        "persistent": false
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    2 popup

    popup可以理解为是一个插件的页面,可以通过页面上提供的方法调用插件里面的功能 如拦截到数据之后下载到本地, 或者请求后端接口拿代理ip进行代理,如果设置了代理,那么后续的请求都会走代理, 点击浏览器插件就会展示如下图对应的页面

    在这里插入图片描述

    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <style>
          #box {
            align-items: center;
          }
          #input-box {
            display: flex;
            align-items: center;
          }
          .label {
            white-space: nowrap;
          }
          .btn-box {
            padding-left: 50px;
            padding-top: 10px;
          }
        style>
      head>
      <body>
    
        <div id="btn-box3">
            <button id="set-proxy-btn">设置代理button>
          div>
            <br>
    
        <div id="btn-box4">
            <button id="open_manage_panel">打开管理面板button>
          div>
        <br>
        <script src="/html/popup/popup.js">script>
      body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    
    function set_proxy() {
        var config = {
    	mode: "fixed_servers",
    	rules: {
    		proxyForHttp: {
    			scheme: "http",
    			host: "127.0.0.1",  // 代理IP, 调用后端接口获取到代理ip的时候将host 和 port配置为对应的地址和端口
    			port: 80 //代理端口
    		},
    		proxyForHttps: {
    			scheme: "http",
    			host: "127.0.0.1",
    			port: 443
    		},
    		bypassList: ["localhost", "127.0.0.1", ""]
    	}
    };
        // 设置代理
    	chrome.proxy.settings.set(
    		{ value: config, scope: 'regular' },
    		function () {
    	        console.log("设置代理成功") });
    	  var originalIp = "";
    	  // 设置代理成功后校验代理是否正常,如果不需要校验也可以去掉下面的代码
    	    async function getCurrentIp() {
    	        let res = await fetch('http://api.ipify.org/');
    	        return await res.text();
    	    }
    	    // Get ip before setting proxy
    	    getCurrentIp().then(ip => {
    	        originalIp = ip;
    	        console.log(originalIp);
    	    });
    
    }
    
    
    function open_tabs() {
        chrome.tabs.create({
            url: './html/manage_panel/manage_panel.html'
        })
    }
    document.getElementById('open_manage_panel').onclick = open_tabs
    document.getElementById('set-proxy-btn').onclick = set_proxy
    // set_proxy()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    可以设置代理,也可以点击管理面板在新的tab上打开一个页面, 如下图
    在这里插入图片描述

    content_script

    content_script 我的理解是一个管理往网页注入js脚本的一个控制程序,负责管理注入到网页的 inject js脚本文件,可以跟被注入到网页的 inject js脚本进行通信,也可以跟程序页面或全局背景js进行通信,后面拦截到的数据主要通过它来获取转发到插件页面上,是一个数据中转、管理注入脚本的一个控制器

    var comment_id_list  = []
    var comment_data = {}
    
    // 转发popup指令  popup => content script => inject script
    chrome.extension.onMessage.addListener(
        function (request, sender, sendResponse) {
          // 将inject拦截到的数据
          if (request.action === "get_show_data") {
                console.log("收到get_show_data请求")
                
                // comment_data 和 comment_id_list 是下面inject newtwork.js 中拦截到的数据存储变量,在content_script中可以直接使用 inject里面的变量
                sendResponse({comment_data: comment_data, comment_id_list: comment_id_list})
            }
        }
    );
    
    // 监听数据
    window.addEventListener("message", function(e) {
        if (e.data.action === 'comment_data'){
        // 监听message, 将 inject 里面的js 转发的数据存储到本地上
            comment_data = e.data.comment_data
            comment_id_list = e.data.comment_id_list
        }
    
    }, false);
    
    
    // 插件启动的时候给background 全局对象发送一个请求,目的是让background缓存当前插件的tabid
    chrome.runtime.sendMessage({
        type: "action"
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    inject

    inject 是注入到页面的js文件,可以在这个js里面进行请求响应数据的拦截,上面博主分享的代码里面已经完成了数据的拦截,这里主要记录的是 注入页面的js如何和管理面板进行通信传递数据

    正常来说 inject 里面拦截到的接口数据是不能直接和插件页面通信的,不过可以通过接口请求将数据传送到自己的服务器上, 如下图
    在这里插入图片描述

    在 inject 脚本拦截获取到数据之后,将数据内容直接发到直接的服务器上,需要注意后端服务器是否有跨域请求

    第二种在页面展示数据的方法是 每次拦截到数据之后给content_script 存储起来,content_script 先是通过 chrome.runtime.sendMessage 给 background.js 发送一个数据,让background.js 获取到插件的tabid,然后每次接受到inject 注入的js转发的数据后存储起来,等到tab管理页面需要的时候转发给他

    background.js
    var tabid = ""
    
    // background.js
    chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
        tabid = sender.tab.id
    });
    
    
    function get_tabid(){
        console.log("获取tabid: ", tabid)
        return tabid
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    background.js 是一个全局的对象,在浏览器开始的时候就存在了,它可以接收到 浏览器插件页面的调用,也可以接收到 content_script 给它发送的数据内容,本插件管理页面和content_script 之间的通信是通过 tabid来进行通信的,需要background.js 存储content_script 的tabid,以供 管理页面使用

    管理页面
    DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>管理面板title>
    <link href="/css/style.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="/js/config/jquery.min.js">script>
    
    <script type="text/javascript" src="/js/config/subnav.js">script>
    <script type="text/javascript" src="/html/manage_panel/manage_panel.js">script>
    head>
    <body>
    <div  style="overflow: hidden; width: 80%; margin: auto">
    
    <br/>
    <textarea rows="50" cols="250" id="text" style="padding: 40px; margin: auto; width: 100%;display: block">textarea>
    div>
    body>
    html>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    $(function() {
    	// 获取background对象
    	const background = chrome.extension.getBackgroundPage();
    	// 获取这个background  全局对象中存储的插件作用的tabid
    	var tabid = background.get_tabid()
    
        // 获取插件的数据并渲染到html上    	
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
    		{
    			chrome.tabs.sendMessage(tabid, {"action": "get_show_data"}, function(response)
    			{
                    console.log("rrrrrrrrrrr", response)
    				var new_comment_data = response.comment_data
    				var new_comment_id_list = response.comment_id_list
    				var html = ""
    				for (let i = 0; i < new_comment_id_list.length; i++) {
                       
                  html += `\n ${JSON.stringify(new_comment_data[new_comment_id_list[i]])} \n\n\n\n`
                       
    				}
                  
                    document.getElementById("text").value += html
    
    			});
    		});
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    最后的结果就类似这样
    在这里插入图片描述

  • 相关阅读:
    Python核心数据类型
    【微信小程序】实现组件间代码共享的behaviors
    【Java】基础简单好题好思路总结
    【Redis基础篇】学习笔记+操作步骤
    Python 写网络监控
    在MySQL中使用MD5加密【入门体验】
    带你了解基于Ploto构建自动驾驶平台
    java毕业生设计在线学习辅导与答疑系统计算机源码+系统+mysql+调试部署+lw
    MySQL 快速入门之MySQL 5.7.21解压版安装详细教程
    并发编程(概念简述)
  • 原文地址:https://blog.csdn.net/qq_42031243/article/details/133702741