如何编写Tampermonkey脚本并定位moat.com品牌数据库
Hey there! Let's walk through how to tackle this problem—first finding that elusive brand JSON database, then setting up a local image matching system if needed.
Step 1: Hunting for the Brand JSON Database
Most modern sites load dynamic data like brand lists via API calls or embedded scripts. Here's how to dig it up:
Use Browser DevTools Network Panel
- Open Chrome/Firefox DevTools (F12 or Ctrl+Shift+I) and go to the Network tab.
- Filter requests by XHR/Fetch (this narrows down API calls that often return JSON).
- Type a test brand name into Moat's search bar and watch for new requests. Look for endpoints with keywords like
brand,search,autocomplete, orcatalog. - Click on each request to check the Response tab—if you see a list of brand objects (with names, image URLs, etc.), that's your target JSON.
Check for Embedded Page State
Some sites embed initial data directly in the HTML as a JavaScript object. Search the page source (Ctrl+U) for patterns like:window.__BRAND_DATA__ = {...}; // or var brandDatabase = [...];You can also use the DevTools Sources tab to search for
JSON.parseor specific brand names to trace where data is being parsed.Decipher Obfuscated JS
If the site's JS is minified/obfuscated, use the DevTools Pretty Print button ({} in the Sources tab) to make it readable. Search for strings like "brand" or "search" to find functions that fetch or process the brand data.
Step 2: Building a Local Image Search Tampermonkey Script
If you can't directly download the full JSON, or want to use local images for matching, here's a framework to implement:
1. First: Collect Brand Data (Legally!)
Before building the search, you'll need a local database of brands and their image signatures. Write a simple Tampermonkey script to scrape (with respect to Moat's terms of service) brand info as you browse:
// ==UserScript== // @name Moat Brand Data Collector // @namespace http://tampermonkey.net/ // @version 0.1 // @description Collect brand names and image URLs // @author You // @match https://moat.com/* // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // Adjust selectors based on Moat's actual HTML structure const brandElements = document.querySelectorAll('.brand-item'); const brandData = GM_getValue('moatBrands', []); brandElements.forEach(el => { const brandName = el.querySelector('.brand-name').textContent.trim(); const brandImage = el.querySelector('.brand-image').src; if (!brandData.find(b => b.name === brandName)) { brandData.push({name: brandName, imageUrl: brandImage}); } }); GM_setValue('moatBrands', brandData); console.log('Updated brand database:', brandData); })();
2. Implement Image Matching Logic
For local image search, use Perceptual Hashing (pHash) to compare image similarity. Here's a simplified pHash implementation you can use:
function getImageHash(img) { // Resize image to 8x8 grayscale const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 8; canvas.height = 8; ctx.drawImage(img, 0, 0, 8, 8); // Convert to grayscale const data = ctx.getImageData(0, 0, 8, 8).data; const grayscale = []; for (let i = 0; i < data.length; i += 4) { const gray = (data[i] + data[i+1] + data[i+2]) / 3; grayscale.push(gray); } // Calculate average brightness const avg = grayscale.reduce((a, b) => a + b, 0) / grayscale.length; // Generate hash string let hash = ''; grayscale.forEach(pixel => { hash += pixel >= avg ? '1' : '0'; }); return hash; } // Calculate Hamming distance (lower = more similar) function hammingDistance(hash1, hash2) { let distance = 0; for (let i = 0; i < hash1.length; i++) { if (hash1[i] !== hash2[i]) distance++; } return distance; }
3. Integrate with Moat's Search
Replace Moat's default search with your local image matching:
// ==UserScript== // @name Moat Local Image Search // @namespace http://tampermonkey.net/ // @version 0.1 // @description Use local images to search Moat brands // @author You // @match https://moat.com/* // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // Add a file input for local images const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = 'image/*'; fileInput.style.cssText = 'position:fixed; top:20px; right:20px; z-index:9999;'; document.body.appendChild(fileInput); // Load local brand database const brandData = GM_getValue('moatBrands', []); const brandHashes = []; // Precompute hashes for brand images brandData.forEach(brand => { const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => { brandHashes.push({name: brand.name, hash: getImageHash(img)}); }; img.src = brand.imageUrl; }); // Handle local image upload fileInput.addEventListener('change', (e) => { const file = e.target.files[0]; const img = new Image(); img.src = URL.createObjectURL(file); img.onload = () => { const localHash = getImageHash(img); let bestMatch = null; let minDistance = Infinity; // Find most similar brand brandHashes.forEach(brand => { const distance = hammingDistance(localHash, brand.hash); if (distance < minDistance) { minDistance = distance; bestMatch = brand; } }); if (bestMatch) { const similarity = Math.round(100 - (minDistance / 64) * 100); alert(`Best match: ${bestMatch.name}\nSimilarity: ${similarity}%`); // Auto-fill Moat's search bar with the match const searchInput = document.querySelector('#search-input'); if (searchInput) searchInput.value = bestMatch.name; } else { alert('No matching brand found'); } }; }); // Include getImageHash and hammingDistance functions here })();
Important Notes
- Respect Terms of Service: Make sure scraping or modifying Moat's functionality doesn't violate their terms. Always check their legal pages first.
- CORS Issues: If loading brand images from Moat's domain causes errors, download images locally or use a proxy to avoid cross-origin restrictions.
- Performance: For large databases, precompute and store hashes locally (using
GM_setValue) instead of calculating them every time.
内容的提问来源于stack exchange,提问作者joeyr2




