n8n-nodes-duckduckgo-search
Version:
A powerful and comprehensive n8n community node that seamlessly integrates DuckDuckGo search capabilities into your workflows. Search the web, find images, discover news, and explore videos - all with privacy-focused, reliable results.
165 lines (164 loc) • 6.86 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSafeSearchString = exports.directImageSearch = exports.directWebSearch = void 0;
const axios_1 = __importDefault(require("axios"));
function cleanText(text) {
return text
.replace(/ /g, ' ')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, "'")
.replace(/'/g, "'")
.replace(/<\/?b>/g, '')
.replace(/<[^>]*>/g, '')
.replace(/\s+/g, ' ')
.trim();
}
async function directWebSearch(query, options = {}) {
try {
const response = await axios_1.default.post('https://html.duckduckgo.com/html/', new URLSearchParams({
q: query,
b: '',
kl: options.locale || 'us-en',
kp: options.safeSearch === 'strict' ? '1' : options.safeSearch === 'moderate' ? '-1' : '-2',
}), {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
},
timeout: 10000,
});
const results = [];
const html = response.data;
const resultSections = html.split(/<div[^>]*class="[^"]*result results_links[^"]*"[^>]*>/);
for (let i = 1; i < resultSections.length; i++) {
const section = resultSections[i];
const titleMatch = section.match(/<h2[^>]*class="result__title"[^>]*>[\s\S]*?<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/);
const snippetMatch = section.match(/<a[^>]*class="result__snippet"[^>]*href="[^"]*"[^>]*>(.*?)<\/a>/);
if (titleMatch) {
const url = titleMatch[1];
const title = titleMatch[2];
const description = snippetMatch ? snippetMatch[1] : '';
if (title && url && url.startsWith('http')) {
results.push({
title: cleanText(title),
url: url,
description: cleanText(description),
});
if (options.maxResults && results.length >= options.maxResults) {
break;
}
}
}
}
return { results };
}
catch (error) {
console.error('Direct web search error:', error);
throw new Error(`Direct web search failed: ${error.message}`);
}
}
exports.directWebSearch = directWebSearch;
async function directImageSearch(query, options = {}) {
try {
const searchParams = new URLSearchParams({
q: query,
iax: 'images',
ia: 'images',
});
const searchUrl = `https://duckduckgo.com/?${searchParams.toString()}`;
const response = await axios_1.default.get(searchUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
},
timeout: 10000,
});
const vqdMatch = response.data.match(/vqd=([\d-]+)/);
const vqd = vqdMatch ? vqdMatch[1] : null;
if (!vqd) {
const webResults = await directWebSearch(`${query} images photos`, options);
return {
results: webResults.results.map(result => ({
title: result.title,
url: result.url,
thumbnail: '',
source: result.url,
})),
};
}
const imageParams = new URLSearchParams({
l: options.locale || 'us-en',
o: 'json',
q: query,
vqd: vqd,
f: ',,,,,',
p: options.safeSearch === 'strict' ? '1' : options.safeSearch === 'moderate' ? '-1' : '-2',
});
const imageResponse = await axios_1.default.get(`https://duckduckgo.com/i.js?${imageParams.toString()}`, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Referer': searchUrl,
'X-Requested-With': 'XMLHttpRequest',
},
timeout: 10000,
});
const imageData = imageResponse.data;
const results = [];
if (imageData && imageData.results) {
for (const item of imageData.results) {
results.push({
title: item.title || '',
url: item.image || '',
thumbnail: item.thumbnail || '',
width: item.width,
height: item.height,
source: item.url || '',
});
if (options.maxResults && results.length >= options.maxResults) {
break;
}
}
}
return { results };
}
catch (error) {
console.error('Direct image search error:', error);
const webResults = await directWebSearch(`${query} images photos`, options);
return {
results: webResults.results.map(result => ({
title: result.title,
url: result.url,
thumbnail: '',
source: result.url,
})),
};
}
}
exports.directImageSearch = directImageSearch;
function getSafeSearchString(value) {
switch (value) {
case 2:
return 'strict';
case 1:
return 'moderate';
default:
return 'off';
}
}
exports.getSafeSearchString = getSafeSearchString;