手把手教你开发浏览器插件-保存页面元素数据为json或csv

主要功能是获取页面列表元素数据并保存为JSON或CSV格式。
插件包含manifest.json配置文件、后台脚本background.js、内容脚本content.js、弹出窗口popup.html及其相关JS/CSS文件。
核心功能通过changePageColor函数实现,该函数提取页面中特定class的元素数据,支持JSON和CSV两种导出格式。

文件夹目录结构
在这里插入图片描述
icon.png
在这里插入图片描述

插件配置文件 manifest.json

{
  "manifest_version": 3,
  "name": "我的第一个扩展",
  "version": "1.0",
  "description": "一个简单的浏览器扩展示例",
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icon16.png"
    }
  },
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "icons": {
    "16": "icon16.png"
  },
  "content_scripts": [
    {
      "matches": [
        "<all_urls>"
      ],
      "js": [
        "content.js"
      ]
    }
  ]
}

background.js

// 后台脚本,持续运行的扩展进程
console.log("后台脚本已加载");

// 示例:监听浏览器事件
chrome.runtime.onInstalled.addListener(() => {
    console.log("扩展已安装或更新");
});

// 监听来自内容脚本的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    console.log("收到消息:", message);
    sendResponse({reply: "消息已收到"});
});

content.js

console.log("内容脚本已加载到当前页面");

// 示例:在所有页面上添加一个小标记
const marker = document.createElement('div');
marker.textContent = '我的扩展已加载-王帆';
marker.style.position = 'fixed';
marker.style.bottom = '10px';
marker.style.right = '10px';
marker.style.backgroundColor = 'yellow';
marker.style.padding = '5px';
marker.style.zIndex = '9999';
document.body.appendChild(marker);

popup.html

<!DOCTYPE html>
<html>
<meta charset="utf-8"></meta>
<head>
    <style>
        body {
            width: 300px;
            padding: 10px;
            font-family: Arial, sans-serif;
        }

        #box {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
        }

        button {
            padding: 8px 12px;
            color: white;
            border: 3px solid; /* 初始边框设为透明 */
            border-radius: 4px;
            cursor: pointer;
            flex: 1 0 45%;
            animation: borderBreathing 2s infinite ease-in-out; /* 应用呼吸灯效果 */
        }

        /* 为每个按钮添加独特的动画 */
        #changeColor {
            background-color: #1e9fff;
            border-color: #1e9fff;
            animation-name: borderBreathingBlue;
        }

        #getElementData {
            background-color: #ffb800;
            border-color: #ffb800;
            animation-name: borderBreathingYellow;
        }

        #getTitle {
            background-color: #ff5722;
            border-color: #ff5722;
            animation-name: borderBreathingRed;
        }

        /* 呼吸灯效果动画 */
        @keyframes borderBreathing {
            0% {
                border-color: transparent; /* 初始时边框透明 */
            }
            25% {
                border-color: #ff00ff; /* 在25%时变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            50% {
                border-color: currentColor; /* 中间时边框变为当前按钮的颜色 */
                box-shadow: 0 0 6px 2px currentColor; /* 增加按钮的颜色阴影效果 */
            }
            75% {
                border-color: #ff00ff; /* 在75%时再次变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            100% {
                border-color: transparent; /* 结束时边框透明 */
                box-shadow: 0 0 6px 2px rgba(0, 0, 0, 0); /* 恢复无阴影 */
            }
        }

        /* 为每种颜色分别定制呼吸灯效果 */
        @keyframes borderBreathingBlue {
            0% {
                border-color: transparent;
            }
            25% {
                border-color: #ff00ff; /* 在25%时变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            50% {
                border-color: #1e9fff; /* 中间时变为蓝色 */
                box-shadow: 0 0 6px 2px #1e9fff;
            }
            75% {
                border-color: #ff00ff; /* 在75%时再次变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            100% {
                border-color: transparent;
                box-shadow: 0 0 6px 2px rgba(0, 0, 0, 0);
            }
        }

        @keyframes borderBreathingYellow {
            0% {
                border-color: transparent;
            }
            25% {
                border-color: #ff00ff; /* 在25%时变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            50% {
                border-color: #ffb800; /* 中间时变为黄色 */
                box-shadow: 0 0 6px 2px #ffb800;
            }
            75% {
                border-color: #ff00ff; /* 在75%时再次变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            100% {
                border-color: transparent;
                box-shadow: 0 0 6px 2px rgba(0, 0, 0, 0);
            }
        }

        @keyframes borderBreathingRed {
            0% {
                border-color: transparent;
            }
            25% {
                border-color: #ff00ff; /* 在25%时变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            50% {
                border-color: #ff5722; /* 中间时变为红色 */
                box-shadow: 0 0 6px 2px #ff5722;
            }
            75% {
                border-color: #ff00ff; /* 在75%时再次变为紫色 */
                box-shadow: 0 0 6px 2px #ff00ff; /* 增加紫色的阴影效果 */
            }
            100% {
                border-color: transparent;
                box-shadow: 0 0 6px 2px rgba(0, 0, 0, 0);
            }
        }

    </style>
    <title>扩展</title>
</head>
<body>
<h2>我的扩展</h2>
<p>这是一个简单的浏览器扩展</p>
<div id="box">
    <button id="changeColor">改变页面颜色</button>
    <button id="getElementData">获取元素数据</button>
    <button id="getTitle">获取页面标题</button>
</div>
<!-- 浏览器插件-新版本(采集数据demo) -->
<script src="popup.js"></script>
</body>
</html>

popup.js

document.getElementById('changeColor').addEventListener('click', async () => {
    try {
        console.log("按钮点击事件触发");

        const tabsAll = await chrome.tabs.query({});

        const tabs = await chrome.tabs.query({active: true, currentWindow: true});
        if (!tabs || tabs.length === 0) {
            throw new Error("没有找到活动标签页");
        }

        const tab = tabs[0];
        if (!tab.id) {
            throw new Error("标签页ID无效");
        }

        console.log("准备执行脚本,标签页ID:", tab.id);

        await chrome.scripting.executeScript({
            target: {tabId: tab.id},
            func: changePageColor,
            args: [tabsAll, tab]
        });

        console.log("脚本执行成功");
    } catch (error) {
        console.error("执行脚本失败:", error);
        // 可选:在弹出窗口中显示错误信息
        const errorElement = document.createElement('div');
        errorElement.style.color = 'red';
        errorElement.textContent = `错误: ${error.message}`;
        document.body.appendChild(errorElement);
    }
});


function changePageColor(tabs, tab) {
    console.log(tabs);
    console.log(tab);
    try {
        const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
        console.log("正在改变背景颜色为:", randomColor);
        document.body.style.backgroundColor = randomColor;
        chrome.runtime.sendMessage({action: 'someAction', data: 'myData'}, (response) => {
            console.log('收到背景页的响应:', response.reply);
        });
    } catch (error) {
        console.error("内容脚本执行出错:", error);
    }
}

document.getElementById('getElementData').addEventListener('click', async () => {
    try {
        console.log("按钮点击事件触发");

        const tabsAll = await chrome.tabs.query({});

        const tabs = await chrome.tabs.query({active: true, currentWindow: true});
        if (!tabs || tabs.length === 0) {
            throw new Error("没有找到活动标签页");
        }

        const tab = tabs[0];
        if (!tab.id) {
            throw new Error("标签页ID无效");
        }

        console.log("准备执行脚本,标签页ID:", tab.id);

        await chrome.scripting.executeScript({
            target: {tabId: tab.id},
            func: getPageElementData,
            args: [tabsAll, tab]
        });

        console.log("脚本执行成功");
    } catch (error) {
        console.error("执行脚本失败:", error);
        // 可选:在弹出窗口中显示错误信息
        const errorElement = document.createElement('div');
        errorElement.style.color = 'red';
        errorElement.textContent = `错误: ${error.message}`;
        document.body.appendChild(errorElement);
    }
});

function getPageElementData(tabs, tab) {
    console.log(tabs);
    console.log(tab);
    try {
        // 获取 ul class="datu" 的所有 li 元素
        const datuList = document.querySelectorAll('.datu li');
        console.log(datuList)
        // 循环获取 div class="datu-mingzi" 为药品名称 div class="datu-compamy" 为厂家
        const datuInfo = [];
        for (let i = 0; i < datuList.length; i++) {
            const datu = datuList[i];
            const mingzi = datu.querySelector('.datu-mingzi');
            datuInfo.push({
                mingzi: mingzi.innerText
            });
        }
        console.log(datuInfo);
        // 将数据保存为 JSON 文件
        // const blob = new Blob([JSON.stringify(datuInfo, null, 2)], { type: 'application/json' });
        // const link = document.createElement('a');
        // link.href = URL.createObjectURL(blob);
        // link.download = 'datuInfo.json';
        // link.click();


        // 转换 JSON 数据为 CSV
        function convertToCSV(jsonData) {
            const keys = Object.keys(jsonData[0]);  // 获取对象的键名(表头)
            const header = keys.join(',');          // 生成表头
            const rows = jsonData.map(row => keys.map(key => row[key]).join(','));  // 生成每行数据

            return [header, ...rows].join('\n');  // 返回最终的 CSV 字符串
        }

        const csvData = convertToCSV(datuInfo);
        // 创建 CSV 文件并下载
        const blob = new Blob([csvData], {type: 'text/csv'});
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = 'datuInfo.csv';
        link.click();

        chrome.runtime.sendMessage({action: 'someAction', data: 'myData'}, (response) => {
            console.log('收到背景页的响应:', response.reply);
        });

    } catch (error) {
        console.error("内容脚本执行出错:", error);
    }
}

document.getElementById('getTitle').addEventListener('click', async () => {
    try {
        console.log("按钮点击事件触发");

        const tabsAll = await chrome.tabs.query({});

        const tabs = await chrome.tabs.query({active: true, currentWindow: true});
        if (!tabs || tabs.length === 0) {
            throw new Error("没有找到活动标签页");
        }

        const tab = tabs[0];
        if (!tab.id) {
            throw new Error("标签页ID无效");
        }

        console.log("准备执行脚本,标签页ID:", tab.id);

        await chrome.scripting.executeScript({
            target: {tabId: tab.id},
            func: getPageTitle,
            args: [tabsAll, tab]
        });

        console.log("脚本执行成功");
    } catch (error) {
        console.error("执行脚本失败:", error);
        // 可选:在弹出窗口中显示错误信息
        const errorElement = document.createElement('div');
        errorElement.style.color = 'red';
        errorElement.textContent = `错误: ${error.message}`;
        document.body.appendChild(errorElement);
    }
});

function getPageTitle(tabs, tab) {
    console.log(tabs);
    console.log(tab);
    try {
        // 获取页面标题
        const pageTitle = document.title;
        console.log('页面标题:', pageTitle);

        chrome.runtime.sendMessage({action: 'someAction', data: 'myData'}, (response) => {
            console.log('收到背景页的响应:', response.reply);
        });
    } catch (error) {
        console.error("内容脚本执行出错:", error);
    }
}

styles.css

body{
    font-family: Arial;
    width:250px;
    padding:10px;
    background:#f8f9fa;
}

button{
    background:#007bff;
    border: none;
    color: white;
    padding:10px;
    width:100%;
    cursor: pointer;
    border-radius:5px;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值