← back to Wine Finder Next
lib/services/wineAuctionScraper.js
352 lines
/**
* WINE AUCTION & PREMIUM RETAILER SCRAPER
* Tracks expensive wines from multiple sources
* Focuses on $20+ bottles
*/
const axios = require('axios');
const cheerio = require('cheerio');
class WineAuctionScraper {
constructor() {
this.userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
}
/**
* Search The Wine House (premium retailer with $20-$10,000+ bottles)
*/
async searchWineHouse(query) {
try {
const url = `https://www.winehouseliquor.com/search?q=${encodeURIComponent(query)}`;
const response = await axios.get(url, {
headers: {
'User-Agent': this.userAgent,
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'en-US,en;q=0.9'
},
timeout: 10000
});
const $ = cheerio.load(response.data);
const wines = [];
// Parse Wine House results
$('.product-item, .product-card, .product').each((i, el) => {
const name = $(el).find('.product-title, .product-name, h3, h4').first().text().trim();
const priceText = $(el).find('.price, .product-price, [class*="price"]').first().text().trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
const imageUrl = $(el).find('img').first().attr('src');
const productUrl = $(el).find('a').first().attr('href');
if (name && price && price >= 20) {
wines.push({
name,
price,
vintage: null,
image: imageUrl || '',
url: productUrl && productUrl.startsWith('http') ? productUrl : `https://www.winehouseliquor.com${productUrl}`,
source: 'The Wine House',
availability: 'In Stock',
winery: this.extractWinery(name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
});
}
});
return { wines, total: wines.length, source: 'winehouse' };
} catch (error) {
console.error('Wine House error:', error.message);
return { wines: [], total: 0, source: 'winehouse', error: error.message };
}
}
/**
* Search JJ Buckley Fine Wines (high-end specialty retailer)
*/
async searchJJBuckley(query) {
try {
const url = `https://www.jjbuckley.com/catalogsearch/result/?q=${encodeURIComponent(query)}`;
const response = await axios.get(url, {
headers: {
'User-Agent': this.userAgent,
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'en-US,en;q=0.9'
},
timeout: 10000
});
const $ = cheerio.load(response.data);
const wines = [];
// Parse JJ Buckley results
$('.product-item, .item, .product').each((i, el) => {
const name = $(el).find('.product-name, .product-title, h2, h3').first().text().trim();
const priceText = $(el).find('.price, [data-price-type="finalPrice"]').first().text().trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
const imageUrl = $(el).find('img').first().attr('src');
const productUrl = $(el).find('a').first().attr('href');
if (name && price && price >= 20) {
wines.push({
name,
price,
vintage: null,
image: imageUrl || '',
url: productUrl || '',
source: 'JJ Buckley Fine Wines',
availability: 'Available',
winery: this.extractWinery(name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
});
}
});
return { wines, total: wines.length, source: 'jjbuckley' };
} catch (error) {
console.error('JJ Buckley error:', error.message);
return { wines: [], total: 0, source: 'jjbuckley', error: error.message };
}
}
/**
* Search WineBid.com (largest online wine auction)
*/
async searchWineBid(query) {
try {
const url = `https://www.winebid.com/BuyWine?keyword=${encodeURIComponent(query)}`;
const response = await axios.get(url, {
headers: { 'User-Agent': this.userAgent },
timeout: 10000
});
const $ = cheerio.load(response.data);
const wines = [];
// Parse auction listings
$('.auction-item').each((i, el) => {
const name = $(el).find('.wine-name').text().trim();
const priceText = $(el).find('.current-bid').text().trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
const vintage = $(el).find('.vintage').text().trim();
const imageUrl = $(el).find('img').attr('src');
const auctionUrl = 'https://www.winebid.com' + $(el).find('a').attr('href');
if (name && price) {
wines.push({
name,
price,
vintage: parseInt(vintage) || null,
image: imageUrl || '',
url: auctionUrl,
source: 'WineBid Auction',
availability: 'Auction Active',
winery: this.extractWinery(name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
});
}
});
return { wines, total: wines.length, source: 'winebid' };
} catch (error) {
console.error('WineBid scrape error:', error.message);
return { wines: [], total: 0, source: 'winebid', error: error.message };
}
}
/**
* Search K&L Wine Auctions (premium selections)
*/
async searchKLAuctions(query) {
try {
// K&L redirects auction traffic to Heritage Auctions
const url = `https://finewine.ha.com/c/search.zx?N=0&Ntt=${encodeURIComponent(query)}`;
const response = await axios.get(url, {
headers: { 'User-Agent': this.userAgent },
timeout: 10000
});
const $ = cheerio.load(response.data);
const wines = [];
// Parse Heritage auction results
$('.lot-tile').each((i, el) => {
const name = $(el).find('.lot-title').text().trim();
const priceText = $(el).find('.current-bid, .estimate').text().trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
const imageUrl = $(el).find('img').attr('src');
const lotUrl = $(el).find('a').attr('href');
if (name && price) {
wines.push({
name,
price,
vintage: null,
image: imageUrl || '',
url: lotUrl,
source: 'Heritage Wine Auction',
availability: 'Auction',
winery: this.extractWinery(name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
});
}
});
return { wines, total: wines.length, source: 'heritage' };
} catch (error) {
console.error('Heritage auction error:', error.message);
return { wines: [], total: 0, source: 'heritage', error: error.message };
}
}
/**
* Search Zachys Wine Auctions (premium rare wines)
*/
async searchZachys(query) {
try {
const url = `https://www.zachys.com/search?q=${encodeURIComponent(query)}`;
const response = await axios.get(url, {
headers: { 'User-Agent': this.userAgent },
timeout: 10000
});
const $ = cheerio.load(response.data);
const wines = [];
// Parse Zachys auction results
$('.product-item, .lot-item').each((i, el) => {
const name = $(el).find('.product-name, .lot-name').text().trim();
const priceText = $(el).find('.price, .estimate').text().trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
const imageUrl = $(el).find('img').attr('src');
const lotUrl = $(el).find('a').attr('href');
if (name && price && price >= 20) {
wines.push({
name,
price,
vintage: null,
image: imageUrl ? `https://www.zachys.com${imageUrl}` : '',
url: lotUrl ? `https://www.zachys.com${lotUrl}` : '',
source: 'Zachys Wine Auctions',
availability: 'Auction',
winery: this.extractWinery(name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
});
}
});
return { wines, total: wines.length, source: 'zachys' };
} catch (error) {
console.error('Zachys auction error:', error.message);
return { wines: [], total: 0, source: 'zachys', error: error.message };
}
}
/**
* Search Acker Wines (world's largest rare wine auction house)
*/
async searchAcker(query) {
try {
const url = `https://www.ackerwines.com/search?q=${encodeURIComponent(query)}`;
const response = await axios.get(url, {
headers: { 'User-Agent': this.userAgent },
timeout: 10000
});
const $ = cheerio.load(response.data);
const wines = [];
// Parse Acker auction results
$('.wine-item, .product').each((i, el) => {
const name = $(el).find('.wine-name, .product-title').text().trim();
const priceText = $(el).find('.price, .current-bid').text().trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
const imageUrl = $(el).find('img').attr('src');
const lotUrl = $(el).find('a').attr('href');
if (name && price && price >= 20) {
wines.push({
name,
price,
vintage: null,
image: imageUrl || '',
url: lotUrl ? `https://www.ackerwines.com${lotUrl}` : '',
source: 'Acker Wines',
availability: 'Available',
winery: this.extractWinery(name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
});
}
});
return { wines, total: wines.length, source: 'acker' };
} catch (error) {
console.error('Acker auction error:', error.message);
return { wines: [], total: 0, source: 'acker', error: error.message };
}
}
/**
* Aggregate search across all sources
* Focuses on premium wine retailers with $20+ inventory
*/
async searchAll(query) {
const results = await Promise.allSettled([
this.searchWineHouse(query),
this.searchJJBuckley(query)
// Note: Wine auction sites (WineBid, Zachys, Acker, Wine-Searcher) all block scraping
]);
const wines = [];
results.forEach(result => {
if (result.status === 'fulfilled' && result.value.wines) {
wines.push(...result.value.wines);
}
});
// Filter: Only wines $20+
const expensiveWines = wines.filter(w => w.price >= 20);
return {
wines: expensiveWines,
total: expensiveWines.length,
sources: ['winehouse', 'jjbuckley']
};
}
/**
* Extract winery name from full wine name
*/
extractWinery(fullName) {
// Try to extract first part before year or comma
const match = fullName.match(/^([^,0-9]+)/);
return match ? match[1].trim() : fullName.split(' ')[0];
}
}
module.exports = new WineAuctionScraper();