You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展开发报错:无法建立连接,接收端不存在问题求助

Chrome扩展问题:页面抓取与点击功能报错解决

问题场景

开发了一款Chrome扩展,用于抓取example.com网站并在内部页面执行点击功能。新标签页能成功创建,但持续出现两个错误:

  • Port disconnected.Content script may not be injected.
  • Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.

扩展文件代码

1. popup.html

<link href="popup.css" rel="stylesheet" type="text/css" />
<script type="module" src="popup.js"></script>

<div class="container">
    <button class="btnStart" id="btnStart">Start</button>
</div>

2. popup.js

import { getActiveTabURL } from "./utils.js";

document.addEventListener("DOMContentLoaded", async () => {
  const activeTab = await getActiveTabURL();

  if (!activeTab.url.includes("example.com")) {
    const container = document.querySelector(".container");
    container.innerHTML = '<div class="title">This is not a example website page.</div>';
    return;
  } else {
      document.getElementById('btnStart').addEventListener('click', function () {
      chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        const tabId = tabs[0].id;
        chrome.tabs.sendMessage(tabId, { action: "BEGIN", message: "BEGIN", urlPath: activeTab.url, tabId: tabId }, function (response) {
          console.log("0) outer response in popup.js main script file")
          console.log(response);

          chrome.runtime.sendMessage({ tabId: response.tabId, message: "START", urlPath: response.urlPath }, function (inner) {
            console.log("1) inner response in popup.js main script file")
            console.log(inner);
          });
        });
      });
    });
  }
});

3. contentScript.js

const tryConnect = () => {
    chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
        if (msg.action === "scrapeAndClick") {
            console.log("Scraping and Clicking...");

            const messageButtonParent = document.querySelector(".profile-action-compose-option");
            const messageButton = messageButtonParent ? messageButtonParent.querySelector("button") : null;

            if (messageButton) {
                console.log(messageButton.innerText);
            } else {
                console.error("Button not found");
            }

            sendResponse({ complete: true, result: "Scrape and click completed" });
        }

        if (msg.action === "BEGIN") {
            const urlPath = msg.urlPath;
            const tabId = msg.tabId;

            const activeTabPageResultsContainer = document.getElementsByClassName("reusable-search__result-container");
            let individualResultContainer;
            let individualProfileContainer;
            const profileLinks = [];

            for (var i = 0; i < activeTabPageResultsContainer.length; i++) {
                individualResultContainer = activeTabPageResultsContainer[i];
                individualProfileContainer = individualResultContainer.querySelector('.app-aware-link');
                profileLinks.push(individualProfileContainer.getAttribute("href"));
            }

            const item = profileLinks[0];
            console.log("Profile links collected:", { complete: true, result: "Profile links collected", tabId: tabId, message: "START", urlPath: item });
            sendResponse({ complete: true, result: "Profile links collected", tabId: tabId, message: "START", urlPath: item });
        }
    });
};

setTimeout(tryConnect, 1000);

4. background.js

chrome.runtime.onMessage.addListener((obj, sender, sendResponse) => {
    const message = obj.message;
    const urlPath = obj.urlPath;

    if (message === "START") {
        chrome.tabs.create({ url: urlPath, active: false }, function (tab) {
            console.log(`new tab created dynamically with id: ${tab.id}`);
            return new Promise((resolve, reject) => {
                chrome.tabs.sendMessage(tab.id, { action: "scrapeAndClick", message: "scrapeAndClick", urlPath: urlPath, tabId: tab.id }, function (response) {
                    if (response && response.complete) {
                        console.log(response);
                        resolve(response);
                    } else {
                        console.error('Error in scraping and clicking or response is undefined:', response);
                        reject(response);
                    }
                });
            });
        });

        return true;
    }

    return Promise.resolve();
});

5. manifest.json

{
    "name": "Example page scraping",
    "version": "0.0.1",
    "description": "Scraping and submitting forms",
    "permissions": [
        "storage",
        "tabs"
    ],
    "host_permissions": [
        "https://*.example.com/*"
    ],
    "externally_connectable": {
        "matches": [
            "*://*.example.com/*"
        ]
    },
    "background": {
        "service_worker": "background.js"
    },
    "content_scripts": [
        {
            "matches": [
                "*://*.example.com/*"
            ],
            "js": [
                "contentScript.js"
            ]
        }
    ],
    "web_accessible_resources": [
        {
            "resources": [
                "assets/icon-16_x_16.png",
                "assets/icon-24_x_24.png",
                "assets/icon-32_x_32.png"
            ],
            "matches": [
                "https://*.example.com/*"
            ]
        }
    ],
    "action": {
    "default_icon": {
        "16": "assets/icon-16_x_16.png",
        "24": "assets/icon-24_x_24.png",
        "32": "assets/icon-32_x_32.png"
    },
    "default_title": "Example Scaping and Submission",
    "default_popup": "popup.html"
    },
    "manifest_version": 3
}

错误原因与解决方案

核心问题分析

两个错误本质是新标签页创建后立即发送消息,但内容脚本还未完成注入/页面未加载完成,导致接收端不存在。另外还有几个细节问题:

  1. background.js中创建标签后立即调用sendMessage,此时新标签页的DOM和内容脚本都未加载完毕
  2. contentScript.js用setTimeout延迟注册消息监听,可能导致早期消息丢失
  3. 异步响应处理不符合Chrome扩展的规范要求

具体修复步骤

1. 修复background.js:等待标签加载完成再发送消息

使用chrome.tabs.onUpdated监听标签加载状态,直到页面加载完成(status为complete)再发送消息:

chrome.runtime.onMessage.addListener((obj, sender, sendResponse) => {
    const message = obj.message;
    const urlPath = obj.urlPath;

    if (message === "START") {
        chrome.tabs.create({ url: urlPath, active: false }, (tab) => {
            console.log(`new tab created dynamically with id: ${tab.id}`);
            // 监听标签加载完成
            const listener = (tabId, changeInfo) => {
                if (tabId === tab.id && changeInfo.status === 'complete') {
                    chrome.tabs.onUpdated.removeListener(listener);
                    // 此时发送消息
                    chrome.tabs.sendMessage(tab.id, { action: "scrapeAndClick" }, (response) => {
                        if (response?.complete) {
                            console.log(response);
                            sendResponse(response);
                        } else {
                            console.error('Scraping failed:', response);
                            sendResponse({ complete: false, error: 'Failed to scrape' });
                        }
                    });
                }
            };
            chrome.tabs.onUpdated.addListener(listener);
        });
        // 标记异步响应
        return true;
    }

    return Promise.resolve();
});

2. 修复contentScript.js:移除不必要延迟,规范消息监听

直接注册消息监听,不需要延迟;同时处理异步场景时返回true:

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
    if (msg.action === "scrapeAndClick") {
        // 异步执行操作,确保元素加载完成
        const performAction = async () => {
            console.log("Scraping and Clicking...");
            // 短延迟确保动态元素加载,也可替换为MutationObserver监听元素
            await new Promise(resolve => setTimeout(resolve, 500));
            
            const messageButtonParent = document.querySelector(".profile-action-compose-option");
            const messageButton = messageButtonParent?.querySelector("button");

            if (messageButton) {
                console.log(messageButton.innerText);
                // 执行点击操作
                messageButton.click();
                sendResponse({ complete: true, result: "Scrape and click completed" });
            } else {
                console.error("Button not found");
                sendResponse({ complete: false, error: "Button not found" });
            }
        };

        performAction();
        // 标记异步响应
        return true;
    }

    if (msg.action === "BEGIN") {
        const tabId = msg.tabId;

        const activeTabPageResultsContainer = document.getElementsByClassName("reusable-search__result-container");
        const profileLinks = [];

        for (const container of activeTabPageResultsContainer) {
            const profileLink = container.querySelector('.app-aware-link');
            if (profileLink) profileLinks.push(profileLink.getAttribute("href"));
        }

        const item = profileLinks[0];
        console.log("Profile links collected:", item);
        sendResponse({ complete: true, result: "Profile links collected", tabId, urlPath: item });
    }
});

3. 优化popup.js:添加错误处理逻辑

避免因响应异常导致后续逻辑崩溃:

import { getActiveTabURL } from "./utils.js";

document.addEventListener("DOMContentLoaded", async () => {
  const activeTab = await getActiveTabURL();

  if (!activeTab.url.includes("example.com")) {
    const container = document.querySelector(".container");
    container.innerHTML = '<div class="title">This is not an example website page.</div>';
    return;
  }

  document.getElementById('btnStart').addEventListener('click', () => {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const tabId = tabs[0].id;
      chrome.tabs.sendMessage(tabId, { action: "BEGIN" }, (response) => {
        if (chrome.runtime.lastError) {
          console.error("Failed to send BEGIN message:", chrome.runtime.lastError);
          return;
        }
        console.log("0) outer response in popup.js:", response);

        if (response?.complete && response.urlPath) {
          chrome.runtime.sendMessage({ message: "START", urlPath: response.urlPath }, (inner) => {
            if (chrome.runtime.lastError) {
              console.error("Failed to send START message:", chrome.runtime.lastError);
              return;
            }
            console.log("1) inner response in popup.js:", inner);
          });
        } else {
          console.error("Invalid response from content script:", response);
        }
      });
    });
  });
});

额外注意事项

  • 确保host_permissions覆盖所有需要访问的example.com子域名和路径
  • 对于动态加载的页面元素,建议使用MutationObserver替代固定延迟,确保元素存在后再操作
  • 调试时可在Chrome扩展管理页开启"开发者模式",查看背景页和内容脚本的控制台日志

内容的提问来源于stack exchange,提问作者Goofy Programmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 20:24:52