← back to Wine Finder Next
lib/services/wineAuctionBrowser.js
321 lines
/**
* WINE AUCTION BROWSER SCRAPER
* Uses Puppeteer to bypass anti-bot protection
* Tracks $20-$10,000+ wines from ALL major auction sites
*/
const puppeteer = require('puppeteer');
class WineAuctionBrowser {
constructor() {
this.browser = null;
}
async init() {
if (!this.browser) {
this.browser = await puppeteer.launch({
headless: 'new',
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process'
]
});
}
return this.browser;
}
async close() {
if (this.browser) {
await this.browser.close();
this.browser = null;
}
}
extractWinery(fullName) {
const match = fullName.match(/^([^,0-9]+)/);
return match ? match[1].trim() : fullName.split(' ')[0];
}
/**
* WineBid.com - Largest online wine auction
*/
async searchWineBid(query) {
const browser = await this.init();
const page = await browser.newPage();
try {
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
const url = `https://www.winebid.com/BuyWine?keyword=${encodeURIComponent(query)}`;
await page.goto(url, { waitUntil: 'networkidle2', timeout: 15000 });
const wines = await page.evaluate(() => {
const results = [];
document.querySelectorAll('.product-item, [data-product], .wine-listing').forEach(el => {
const nameEl = el.querySelector('.product-name, .wine-name, h3, h4');
const priceEl = el.querySelector('.price, .current-bid, [class*="price"]');
const imgEl = el.querySelector('img');
const linkEl = el.querySelector('a');
if (nameEl && priceEl) {
const name = nameEl.textContent.trim();
const priceText = priceEl.textContent.trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
if (name && price && price >= 20) {
results.push({
name,
price,
image: imgEl ? imgEl.src : '',
url: linkEl ? linkEl.href : ''
});
}
}
});
return results;
});
await page.close();
return wines.map(w => ({
...w,
vintage: null,
source: 'WineBid Auction',
availability: 'Auction Active',
winery: this.extractWinery(w.name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
}));
} catch (error) {
console.error('WineBid browser error:', error.message);
await page.close();
return [];
}
}
/**
* Zachys Wine Auctions
*/
async searchZachys(query) {
const browser = await this.init();
const page = await browser.newPage();
try {
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
const url = `https://www.zachys.com/search?q=${encodeURIComponent(query)}`;
await page.goto(url, { waitUntil: 'networkidle2', timeout: 15000 });
await page.waitForTimeout(2000); // Wait for DataDome
const wines = await page.evaluate(() => {
const results = [];
document.querySelectorAll('.product, .lot, [data-product-id]').forEach(el => {
const nameEl = el.querySelector('.product-title, .lot-title, h2, h3');
const priceEl = el.querySelector('.price, .estimate');
const imgEl = el.querySelector('img');
const linkEl = el.querySelector('a');
if (nameEl && priceEl) {
const name = nameEl.textContent.trim();
const priceText = priceEl.textContent.trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
if (name && price && price >= 20) {
results.push({
name,
price,
image: imgEl ? imgEl.src : '',
url: linkEl ? linkEl.href : ''
});
}
}
});
return results;
});
await page.close();
return wines.map(w => ({
...w,
vintage: null,
source: 'Zachys Wine Auctions',
availability: 'Auction',
winery: this.extractWinery(w.name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
}));
} catch (error) {
console.error('Zachys browser error:', error.message);
await page.close();
return [];
}
}
/**
* Acker Wines
*/
async searchAcker(query) {
const browser = await this.init();
const page = await browser.newPage();
try {
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
const url = `https://www.ackerwines.com/search?q=${encodeURIComponent(query)}`;
await page.goto(url, { waitUntil: 'networkidle2', timeout: 15000 });
await page.waitForTimeout(2000);
const wines = await page.evaluate(() => {
const results = [];
document.querySelectorAll('.product, .wine-item').forEach(el => {
const nameEl = el.querySelector('.product-name, .wine-name, h3');
const priceEl = el.querySelector('.price');
const imgEl = el.querySelector('img');
const linkEl = el.querySelector('a');
if (nameEl && priceEl) {
const name = nameEl.textContent.trim();
const priceText = priceEl.textContent.trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
if (name && price && price >= 20) {
results.push({
name,
price,
image: imgEl ? imgEl.src : '',
url: linkEl ? linkEl.href : ''
});
}
}
});
return results;
});
await page.close();
return wines.map(w => ({
...w,
vintage: null,
source: 'Acker Wines',
availability: 'Available',
winery: this.extractWinery(w.name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
}));
} catch (error) {
console.error('Acker browser error:', error.message);
await page.close();
return [];
}
}
/**
* Heritage Auctions Fine Wine
*/
async searchHeritage(query) {
const browser = await this.init();
const page = await browser.newPage();
try {
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
const url = `https://finewine.ha.com/c/search.zx?N=0&Ntt=${encodeURIComponent(query)}`;
await page.goto(url, { waitUntil: 'networkidle2', timeout: 15000 });
const wines = await page.evaluate(() => {
const results = [];
document.querySelectorAll('.lot-tile, .lot').forEach(el => {
const nameEl = el.querySelector('.lot-title, h3');
const priceEl = el.querySelector('.current-bid, .estimate');
const imgEl = el.querySelector('img');
const linkEl = el.querySelector('a');
if (nameEl && priceEl) {
const name = nameEl.textContent.trim();
const priceText = priceEl.textContent.trim();
const price = parseFloat(priceText.replace(/[^0-9.]/g, ''));
if (name && price && price >= 20) {
results.push({
name,
price,
image: imgEl ? imgEl.src : '',
url: linkEl ? linkEl.href : ''
});
}
}
});
return results;
});
await page.close();
return wines.map(w => ({
...w,
vintage: null,
source: 'Heritage Wine Auction',
availability: 'Auction',
winery: this.extractWinery(w.name),
region: '',
rating: null,
ratingsCount: 0,
searchQuery: query
}));
} catch (error) {
console.error('Heritage browser error:', error.message);
await page.close();
return [];
}
}
/**
* Search all auction sources in parallel
*/
async searchAll(query) {
try {
const results = await Promise.allSettled([
this.searchWineBid(query),
this.searchZachys(query),
this.searchAcker(query),
this.searchHeritage(query)
]);
const wines = [];
results.forEach(result => {
if (result.status === 'fulfilled' && Array.isArray(result.value)) {
wines.push(...result.value);
}
});
// Filter $20+
const expensiveWines = wines.filter(w => w.price >= 20);
return {
wines: expensiveWines,
total: expensiveWines.length,
sources: ['winebid', 'zachys', 'acker', 'heritage']
};
} catch (error) {
console.error('Browser search error:', error.message);
return { wines: [], total: 0, sources: [] };
}
}
}
module.exports = new WineAuctionBrowser();