← back to Cypress Awards
backend/src/scraper/googleSearch.js
118 lines
const axios = require('axios');
class GoogleCyPresSearch {
constructor() {
this.delay = 1000; // Be polite to Google
}
/**
* Search Google for cy pres pages for a given organization
* Uses site-specific search: site:example.org "cy pres" OR "cypres"
*/
async searchForCyPresPage(domain) {
try {
// Extract domain from URL if full URL provided
const domainOnly = domain.replace(/^https?:\/\//, '').replace(/\/$/, '');
// Build Google search query
const query = `site:${domainOnly} ("cy pres" OR "cypres" OR "cy-pres")`;
const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
console.log(` 🔍 Google searching: ${domainOnly}`);
await this.sleep(this.delay);
const response = await axios.get(searchUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
},
timeout: 10000
});
const html = response.data;
// Extract URLs from search results
// Look for href="/url?q=" pattern in Google results
const urlPattern = /href="\/url\?q=(https?:\/\/[^&"]+)/g;
const matches = [];
let match;
while ((match = urlPattern.exec(html)) !== null) {
const url = decodeURIComponent(match[1]);
// Only include URLs from the target domain
if (url.includes(domainOnly) &&
(url.includes('cy-pres') || url.includes('cypres') || url.includes('cy_pres'))) {
matches.push(url);
}
}
if (matches.length > 0) {
console.log(` ✓ Found ${matches.length} potential cy pres URL(s) via Google`);
return matches[0]; // Return first match
}
return null;
} catch (error) {
// Google might block automated searches - that's okay, we have fallbacks
if (error.response && error.response.status === 429) {
console.log(` ⚠ Google rate limit hit - will use direct search`);
}
return null;
}
}
/**
* Alternative: Use DuckDuckGo (doesn't block as aggressively)
*/
async searchDuckDuckGo(domain) {
try {
const domainOnly = domain.replace(/^https?:\/\//, '').replace(/\/$/, '');
const query = `site:${domainOnly} cy pres`;
const searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
await this.sleep(this.delay);
const response = await axios.get(searchUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
},
timeout: 10000
});
const html = response.data;
// Extract URLs from DuckDuckGo results
const urlPattern = /href="\/\/duckduckgo\.com\/l\/\?uddg=([^&"]+)/g;
const matches = [];
let match;
while ((match = urlPattern.exec(html)) !== null) {
const url = decodeURIComponent(match[1]);
if (url.includes(domainOnly) &&
(url.includes('cy-pres') || url.includes('cypres'))) {
matches.push(url);
}
}
if (matches.length > 0) {
console.log(` ✓ Found via DuckDuckGo: ${matches[0]}`);
return matches[0];
}
return null;
} catch (error) {
return null;
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
module.exports = GoogleCyPresSearch;