Chrome扩展调用本地Python脚本报错“ReferenceError: $ is not defined”求助
先解决你碰到的ReferenceError: $ is not defined错误,然后再梳理Chrome扩展调用本地Python脚本的核心问题——你的现有思路存在本质缺陷:Chrome扩展无法直接通过AJAX调用本地文件系统里的Python脚本,得用官方支持的方式实现。
一、修复jQuery未定义错误
这个错误出现在content.js里,根源是内容脚本(content script)的执行环境和页面完全隔离,你在popup.html里引入的jQuery不会自动作用到内容脚本中;另外popup.html里重复引入jQuery的问题也需要清理。
具体修正步骤:
清理popup.html的脚本引入:
删除本地jquery-3.5.1.min.js的引入(如果没有这个本地文件的话),保留CDN版本即可,同时调整脚本顺序,确保先加载jQuery再加载依赖它的脚本:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Scraper</title> </head> <body> <button>Run Python</button> <!-- 先引入jQuery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <!-- popup.js依赖jQuery,放在后面 --> <script src="popup.js"></script> <!-- content.js是内容脚本,不需要在这里引入!它会通过manifest自动注入到目标页面 --> </body> </html>给内容脚本添加jQuery依赖:
修改manifest.json的content_scripts配置,把jQuery也注入到目标页面中,这样content.js就能正常使用$了:"content_scripts": [ { "matches": ["https://www.amazon.in/*"], "js": ["https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js", "content.js"] } ],(如果需要离线使用扩展,建议下载jQuery文件到本地,替换上述CDN路径)
二、核心问题:Chrome扩展调用本地Python脚本的正确方式
你原来写的$.ajax({url: "./myscraper"})完全行不通——Chrome扩展的AJAX请求只能发送到HTTP/HTTPS服务器,无法直接访问本地文件系统里的脚本。这里提供两种可行的实现方案:
方案1:用Native Messaging(官方推荐,适合桌面端)
Native Messaging是Chrome官方提供的扩展与本地应用通信的方式,步骤如下:
修改Python脚本:让它从标准输入读取Chrome发送的URL,处理后把JSON结果输出到标准输出(遵循Native Messaging的通信规范):
from bs4 import BeautifulSoup as soup import requests import json import sys import struct # 读取Chrome发送的Native Messaging格式消息 def read_message(): raw_length = sys.stdin.read(4) if not raw_length: sys.exit(0) message_length = struct.unpack('=I', raw_length)[0] message = sys.stdin.read(message_length) return json.loads(message) # 向Chrome发送Native Messaging格式消息 def send_message(message): encoded_message = json.dumps(message).encode('utf-8') sys.stdout.write(struct.pack('=I', len(encoded_message))) sys.stdout.write(encoded_message) sys.stdout.flush() # 网页抓取逻辑 def scrape_amazon(url): header = { "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36' } page = requests.get(url, headers=header) page_soup = soup(page.content,'html.parser') title = page_soup.find(id="productTitle").get_text() price = page_soup.find(id="priceblock_ourprice").get_text() available = page_soup.find(id="availability").get_text() avail = available.split(".") availability = avail[0] return { 'NAME': title.strip(), 'PRICE': price.strip(), 'AVAILABILITY': availability.strip(), } if __name__ == "__main__": message = read_message() url = message.get('url') result = scrape_amazon(url) # 保存结果到本地JSON(可选) with open('amazon.json','w') as json_file: json.dump(result, json_file) # 把结果发送回Chrome扩展 send_message(result)创建Native Messaging主机配置文件(比如命名为
com.yourname.scraper.json),注意替换路径和扩展ID:{ "name": "com.yourname.scraper", "description": "Amazon Scraper Native Messaging Host", "path": "你的Python脚本绝对路径", "type": "stdio", "allowed_origins": [ "chrome-extension://<你的扩展ID>/" ] }(扩展ID可以在Chrome扩展管理页面开启开发者模式后查看)
注册Native Messaging主机:
- Windows:在注册表
HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.yourname.scraper中添加项,值为上述配置文件的绝对路径。 - macOS:把配置文件放到
~/Library/Application Support/Google/Chrome/NativeMessagingHosts/目录。 - Linux:把配置文件放到
~/.config/google-chrome/NativeMessagingHosts/目录。
- Windows:在注册表
修改扩展代码调用Native Messaging:
- 更新
popup.js:document.addEventListener('DOMContentLoaded', function() { document.querySelector('button').addEventListener('click', onclick, false) function onclick() { chrome.tabs.query({currentWindow:true, active:true}, function(tabs){ var link = tabs[0].url; // 调用Native Messaging主机 chrome.runtime.sendNativeMessage('com.yourname.scraper', {url: link}, function(response) { if (chrome.runtime.lastError) { alert('调用失败:' + chrome.runtime.lastError.message); } else { alert('抓取结果:\n' + JSON.stringify(response, null, 2)); } }); }) } }, false) - 更新
manifest.json添加权限:"permissions": ["activeTab", "nativeMessaging"],
- 更新
方案2:把Python脚本做成本地HTTP服务器(更简单易上手)
如果觉得Native Messaging配置太繁琐,可以把Python脚本改成一个轻量HTTP服务器,扩展通过AJAX请求这个服务器:
修改Python脚本为Flask服务器:
from bs4 import BeautifulSoup as soup import requests import json from flask import Flask, request app = Flask(__name__) @app.route('/scrape', methods=['POST']) def scrape(): url = request.json.get('url') header = { "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36' } page = requests.get(url, headers=header) page_soup = soup(page.content,'html.parser') title = page_soup.find(id="productTitle").get_text() price = page_soup.find(id="priceblock_ourprice").get_text() available = page_soup.find(id="availability").get_text() avail = available.split(".") availability = avail[0] result = { 'NAME': title.strip(), 'PRICE': price.strip(), 'AVAILABILITY': availability.strip(), } with open('amazon.json','w') as json_file: json.dump(result, json_file) return json.dumps(result) if __name__ == '__main__': app.run(host='localhost', port=5000, debug=True)(需要先安装Flask:
pip install flask)修改扩展的popup.js发送请求:
document.addEventListener('DOMContentLoaded', function() { document.querySelector('button').addEventListener('click', onclick, false) function onclick() { chrome.tabs.query({currentWindow:true, active:true}, function(tabs){ var link = tabs[0].url; $.ajax({ url: 'http://localhost:5000/scrape', method: 'POST', contentType: 'application/json', data: JSON.stringify({url: link}), success: function(response) { alert('抓取结果:\n' + JSON.stringify(response, null, 2)); }, error: function(err) { alert('请求失败,请确保Python服务器已启动:' + err.responseText); } }); }) } }, false)更新manifest.json添加权限:
"permissions": ["activeTab", "http://localhost:5000/*"],
最后注意事项
- 如果你打算升级到Manifest V3(Chrome当前推荐的版本),部分API会有变化,比如
browser_action改成action,content_security_policy格式也需要调整,需要对应适配。 - 运行前请确保Python脚本的依赖(BeautifulSoup、requests、Flask等)已经安装完成。
内容的提问来源于stack exchange,提问作者Omkar 7727

