I am trying to create a Google Chrome extension web scraper, and the idea is a user clicks on the icon and the extension scrapes the current active tab and prints some processed data onto a popup.
To do this, I decided to set in my manifest a default popup.html, and have the popup.js sendMessage to a content.js file, which contains the function that scrapes the webpage. However, during my implementation, I received the following error on popup.html:0 -> Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
I tried looking up my exact issue online, and I found an already existing stack overflow page: Chrome Extension message passing: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist
However, I tried implementing this and the error still persists. I've attached my manifest.JSON, popup.html, popup.js, content.js, and background.js files below:
Manifest.JSON
{
"manifest_version": 3,
"name": "HTML Scraper",
"version": "1.0",
"description": "Scrapes the HTML of the current webpage and prints it to console.",
"permissions": ["storage", "activeTab", "scripting"],
"background": {
"service_worker": "background.js",
"type": "module"
},
"action": {
"default_popup": "popup.html",
"default_icon": "icons/icon16.png"
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
popup.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Popup</title>
<!-- Include popup.js -->
<script src="popup.js"></script>
</head>
<body>
<h1>Hi!</h1>
</body>
</html>
popup.js
if (document.readyState != 'loading') {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {action: 'scrapeHTML'}, function(response) {
console.log('Scrape command sent to content script');
});
});
} else {
document.addEventListener('DOMContentLoaded', function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {action: 'scrapeHTML'}, function(response) {
console.log('Scrape command sent to content script');
});
console.log("tester");
});
});
}
content.js
function ScrapeHTML() {
const htmlcontent = document.body.innerText;
const url = document.location.href;
const containsAmazon = url.includes("amazon.com/");
chrome.runtime.sendMessage({action: 'sentLoad'},{
html: htmlcontent,
containsAmazon: containsAmazon
});
}
document.addEventListener('DOMContentLoaded', function() {
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.action == 'scrapeHTML') {
ScrapeHTML();
} else {
console.log('Unknown message received from popup script:', message);
}
});
});
background.js
// background.js
import {amazonMessage} from './helpers.js';
chrome.runtime.onMessage.addListener((message) => {
if (message.action === 'sentLoad') {
const containsAmazon = message.containsAmazon;
const text = message.html;
if (containsAmazon) {
console.log(amazonMessage(text, "Sponsored", "Products related to this item"));
} else {
console.log(text);
}
chrome.storage.local.set({counter: text}, function() {
console.log("Message saved to local storage.");
});
}
});
You need to declare
content_scriptsin manifest.json, then re-injectcontent_scriptsafter reloading/installing the extension in Chrome, but a much simpler solution is to use programmatic injection viachrome.scripting.executeScriptinstead of content_scripts+messaging, see these examples.popup.html: add
deferto load the script at DOMContentLoaded:popup.js:
No need for the background script or a separate content script for such simple scraping.