← back to Designer Wallcoverings
DW-Agents/dw-agents/amazon-realtime-scraper.ts
279 lines
/**
* Real-time Amazon Price Scraper
* Works WITHOUT Product Advertising API
* Scrapes actual Amazon product pages for real prices
*/
import fetch from 'node-fetch';
import * as cheerio from 'cheerio';
interface AmazonProduct {
title: string;
price: number;
currency: string;
url: string;
asin?: string;
rating?: number;
reviewCount?: number;
available: boolean;
}
interface SearchResult {
query: string;
products: AmazonProduct[];
timestamp: Date;
}
/**
* Search Amazon and scrape real product data
*/
export async function searchAmazonRealTime(query: string, maxResults: number = 10): Promise<SearchResult> {
console.log(`🔍 Searching Amazon for: "${query}"`);
const products: AmazonProduct[] = [];
try {
const searchQuery = encodeURIComponent(query);
const searchUrl = `https://www.amazon.com/s?k=${searchQuery}`;
const response = await fetch(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',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
}
});
if (!response.ok) {
console.error(`❌ Amazon returned status: ${response.status}`);
return { query, products: [], timestamp: new Date() };
}
const html = await response.text();
const $ = cheerio.load(html);
// Find product cards
$('div[data-component-type="s-search-result"]').each((idx, element) => {
if (idx >= maxResults) return false;
try {
const $el = $(element);
// Get ASIN
const asin = $el.attr('data-asin') || '';
// Get title
const titleEl = $el.find('h2 a span, h2 span');
const title = titleEl.first().text().trim();
// Get price - try multiple selectors
let price = 0;
const priceWhole = $el.find('.a-price-whole').first().text().replace(/[^0-9]/g, '');
const priceFraction = $el.find('.a-price-fraction').first().text().replace(/[^0-9]/g, '');
if (priceWhole) {
price = parseFloat(`${priceWhole}.${priceFraction || '00'}`);
}
// Alternative price selector
if (!price) {
const priceText = $el.find('.a-price .a-offscreen').first().text();
const priceMatch = priceText.match(/[\d,]+\.?\d*/);
if (priceMatch) {
price = parseFloat(priceMatch[0].replace(/,/g, ''));
}
}
// Get rating
let rating = undefined;
const ratingText = $el.find('.a-icon-star-small .a-icon-alt').text();
const ratingMatch = ratingText.match(/(\d+\.?\d*)/);
if (ratingMatch) {
rating = parseFloat(ratingMatch[1]);
}
// Get review count
let reviewCount = undefined;
const reviewText = $el.find('span[aria-label]').filter((i, el) => {
const label = $(el).attr('aria-label') || '';
return label.includes('stars');
}).parent().parent().find('span').last().text().trim();
const reviewMatch = reviewText.match(/[\d,]+/);
if (reviewMatch) {
reviewCount = parseInt(reviewMatch[0].replace(/,/g, ''));
}
// Get product URL
const productPath = $el.find('h2 a').attr('href') || '';
const url = productPath ? `https://www.amazon.com${productPath}` : '';
if (title && price > 0 && url) {
products.push({
title,
price,
currency: 'USD',
url,
asin,
rating,
reviewCount,
available: true
});
console.log(` ✅ ${title.substring(0, 50)}... - $${price.toFixed(2)}`);
}
} catch (err) {
console.error(' ⚠️ Error parsing product:', err);
}
});
console.log(`✅ Found ${products.length} products\n`);
} catch (error: any) {
console.error('❌ Error searching Amazon:', error.message);
}
return {
query,
products,
timestamp: new Date()
};
}
/**
* Get product details by ASIN
*/
export async function getProductByAsin(asin: string): Promise<AmazonProduct | null> {
console.log(`🔍 Fetching product: ${asin}`);
try {
const url = `https://www.amazon.com/dp/${asin}`;
const response = await fetch(url, {
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',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5'
}
});
if (!response.ok) {
console.error(`❌ Amazon returned status: ${response.status}`);
return null;
}
const html = await response.text();
const $ = cheerio.load(html);
// Get title
const title = $('#productTitle').text().trim();
// Get price
let price = 0;
const priceWhole = $('.a-price-whole').first().text().replace(/[^0-9]/g, '');
const priceFraction = $('.a-price-fraction').first().text().replace(/[^0-9]/g, '');
if (priceWhole) {
price = parseFloat(`${priceWhole}.${priceFraction || '00'}`);
}
// Get rating
let rating = undefined;
const ratingText = $('.a-icon-star .a-icon-alt').first().text();
const ratingMatch = ratingText.match(/(\d+\.?\d*)/);
if (ratingMatch) {
rating = parseFloat(ratingMatch[1]);
}
// Get review count
let reviewCount = undefined;
const reviewText = $('#acrCustomerReviewText').text();
const reviewMatch = reviewText.match(/([\d,]+)/);
if (reviewMatch) {
reviewCount = parseInt(reviewMatch[0].replace(/,/g, ''));
}
if (title && price > 0) {
console.log(`✅ ${title.substring(0, 50)}... - $${price.toFixed(2)}`);
return {
title,
price,
currency: 'USD',
url,
asin,
rating,
reviewCount,
available: true
};
}
console.log('⚠️ Could not extract product data');
return null;
} catch (error: any) {
console.error('❌ Error fetching product:', error.message);
return null;
}
}
/**
* Compare prices for office supplies
*/
export async function compareOfficeSupplyPrices(itemName: string): Promise<void> {
console.log(`\n💰 Real-Time Price Comparison: ${itemName}\n`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
const results = await searchAmazonRealTime(itemName, 5);
if (results.products.length === 0) {
console.log('⚠️ No products found');
return;
}
// Sort by price (lowest first)
results.products.sort((a, b) => a.price - b.price);
console.log('📊 Live Amazon Prices:\n');
results.products.forEach((product, idx) => {
const badge = idx === 0 ? '🏆 BEST PRICE' : '';
console.log(`${idx + 1}. ${product.title}`);
console.log(` 💵 Price: $${product.price.toFixed(2)} ${badge}`);
if (product.rating) {
console.log(` ⭐ Rating: ${product.rating}/5 (${product.reviewCount?.toLocaleString()} reviews)`);
}
console.log(` 🔗 URL: ${product.url}`);
console.log('');
});
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
}
// Test function
if (require.main === module) {
(async () => {
console.log('🧪 Testing Real-Time Amazon Scraper\n');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
console.log('✅ No API credentials needed!\n');
console.log('✅ Scrapes live data from Amazon.com\n');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
// Test searches
await compareOfficeSupplyPrices('copy paper');
await new Promise(resolve => setTimeout(resolve, 2000));
await compareOfficeSupplyPrices('black ink cartridge hp');
await new Promise(resolve => setTimeout(resolve, 2000));
await compareOfficeSupplyPrices('post-it notes');
})();
}
export { AmazonProduct, SearchResult };