Force new git SHA to avoid CI scientific notation bug. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
109 lines
2.9 KiB
TypeScript
109 lines
2.9 KiB
TypeScript
import puppeteer from 'puppeteer';
|
|
|
|
async function sleep(ms: number): Promise<void> {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
async function main() {
|
|
const browser = await puppeteer.launch({
|
|
headless: true,
|
|
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
});
|
|
|
|
const page = await browser.newPage();
|
|
await page.setViewport({ width: 1920, height: 1080 });
|
|
|
|
await page.setRequestInterception(true);
|
|
page.on('request', (req) => {
|
|
if (['image', 'font', 'media'].includes(req.resourceType())) {
|
|
req.abort();
|
|
} else {
|
|
req.continue();
|
|
}
|
|
});
|
|
|
|
await page.goto('https://shop.bestdispensary.com/brands', {
|
|
waitUntil: 'networkidle2',
|
|
timeout: 60000
|
|
});
|
|
await sleep(3000);
|
|
|
|
// Bypass age gate
|
|
const ageGate = await page.$('[data-testid="age-gate-modal"]');
|
|
if (ageGate) {
|
|
const btn = await page.$('[data-testid="age-gate-submit-button"]');
|
|
if (btn) await btn.click();
|
|
await sleep(2000);
|
|
}
|
|
|
|
// Check Load More button
|
|
const btnInfo = await page.evaluate(() => {
|
|
const btn = document.querySelector('button.collection__load-more');
|
|
if (!btn) return { found: false };
|
|
|
|
const rect = btn.getBoundingClientRect();
|
|
return {
|
|
found: true,
|
|
text: btn.textContent?.trim(),
|
|
visible: rect.width > 0 && rect.height > 0,
|
|
top: rect.top,
|
|
disabled: (btn as HTMLButtonElement).disabled,
|
|
class: btn.className,
|
|
};
|
|
});
|
|
|
|
console.log('Load More button:', btnInfo);
|
|
|
|
// Scroll to button and click
|
|
console.log('\nScrolling to button and clicking...');
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
const btn = await page.$('button.collection__load-more');
|
|
if (!btn) {
|
|
console.log('Button not found');
|
|
break;
|
|
}
|
|
|
|
// Scroll button into view
|
|
await page.evaluate((b) => b.scrollIntoView({ behavior: 'smooth', block: 'center' }), btn);
|
|
await sleep(500);
|
|
|
|
// Check if button is still there and clickable
|
|
const stillThere = await page.evaluate(() => {
|
|
const b = document.querySelector('button.collection__load-more');
|
|
return b ? b.textContent?.trim() : null;
|
|
});
|
|
|
|
if (!stillThere) {
|
|
console.log('Button disappeared - all loaded');
|
|
break;
|
|
}
|
|
|
|
// Click it
|
|
await btn.click();
|
|
console.log(`Click ${i+1}...`);
|
|
await sleep(2000);
|
|
|
|
const count = await page.evaluate(() =>
|
|
document.querySelectorAll('.brands-page__list a[href*="/brand/"]').length
|
|
);
|
|
console.log(` Brands: ${count}`);
|
|
}
|
|
|
|
// Final count
|
|
const brands = await page.evaluate(() => {
|
|
const list: string[] = [];
|
|
document.querySelectorAll('.brands-page__list a[href*="/brand/"]').forEach((a: Element) => {
|
|
list.push(a.textContent?.trim() || '');
|
|
});
|
|
return list;
|
|
});
|
|
|
|
console.log(`\nTotal brands: ${brands.length}`);
|
|
console.log(brands.join(', '));
|
|
|
|
await browser.close();
|
|
}
|
|
|
|
main().catch(console.error);
|