← back to Designer Wallcoverings
Backfill images for 92 imageless Lillian August veneers (Browserbase, wallquest.com/<mfr>), 89/89 scraped 0 miss
de1e25644f9f1a363336dff80180262037b6c2ae · 2026-07-06 16:01:23 -0700 · Steve
Files touched
A scripts/wallquest-refresh/ln-image-scrape.cjs
Diff
commit de1e25644f9f1a363336dff80180262037b6c2ae
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Jul 6 16:01:23 2026 -0700
Backfill images for 92 imageless Lillian August veneers (Browserbase, wallquest.com/<mfr>), 89/89 scraped 0 miss
---
scripts/wallquest-refresh/ln-image-scrape.cjs | 45 +++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/scripts/wallquest-refresh/ln-image-scrape.cjs b/scripts/wallquest-refresh/ln-image-scrape.cjs
new file mode 100644
index 00000000..2ee5f9c0
--- /dev/null
+++ b/scripts/wallquest-refresh/ln-image-scrape.cjs
@@ -0,0 +1,45 @@
+// Backfill images for the 92 imageless Lillian August (LN####) veneers in phillipe_romano_catalog.
+// product page = wallquest.com/<mfr lowercase> (CF-gated → Browserbase). Extracts full-res image,
+// updates image_url + product_url. LIMIT to test small first.
+const Browserbase = require('@browserbasehq/sdk').default;
+const { chromium } = require('playwright-core');
+const { Client } = require('pg');
+require('dotenv').config({ path: require('os').homedir() + '/.claude/skills/browserbase/.env' });
+const CONN = 'postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified';
+const LIMIT = parseInt(process.env.LIMIT || '3', 10);
+const CHUNK = parseInt(process.env.CHUNK || '30', 10);
+const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY });
+const goto = async (p, u) => { for (let i=0;i<3;i++){ try{ await p.goto(u,{waitUntil:'domcontentloaded',timeout:45000}); return 1;}catch(e){await p.waitForTimeout(2500);} } return 0; };
+const extractImg = (p) => p.evaluate(() => {
+ const Q = s => [...document.querySelectorAll(s)];
+ const imgs = [...new Set(Q('a[data-full-image-url]').map(a=>a.getAttribute('data-full-image-url')).concat(Q('#cloudZoomImage,.picture img').map(i=>i.src)))].filter(u=>u&&/thumbs\/\d+_/.test(u)&&/jpe?g|png/i.test(u));
+ return imgs.slice(0,12);
+});
+(async () => {
+ const db = new Client({ connectionString: CONN }); await db.connect();
+ const { rows } = await db.query("SELECT id, mfr_sku FROM phillipe_romano_catalog WHERE created_at::date=CURRENT_DATE AND mfr_sku LIKE 'LN%' AND coalesce(image_url,'')='' ORDER BY id LIMIT $1",[LIMIT]);
+ console.log(`${rows.length} imageless LN veneers to scrape`);
+ let ok=0, miss=0;
+ for (let i=0;i<rows.length;i+=CHUNK) {
+ const chunk = rows.slice(i,i+CHUNK); let br,page;
+ try {
+ const s = await bb.sessions.create({ projectId: process.env.BROWSERBASE_PROJECT_ID, proxies:true, browserSettings:{solveCaptchas:true} });
+ br = await chromium.connectOverCDP(s.connectUrl); page = br.contexts()[0].pages()[0] || await br.contexts()[0].newPage();
+ for (const r of chunk) {
+ const url = 'https://www.wallquest.com/' + r.mfr_sku.toLowerCase();
+ try {
+ if (!await goto(page, url)) { miss++; continue; }
+ await page.waitForTimeout(4000);
+ let imgs = await extractImg(page);
+ if (imgs.length) {
+ await db.query("UPDATE phillipe_romano_catalog SET image_url=$1, all_images=$2, product_url=$3, updated_at=now() WHERE id=$4",[imgs[0], JSON.stringify(imgs), url, r.id]);
+ ok++; if(ok<=3||ok%20===0) console.log(` ✅ ${r.mfr_sku} → ${imgs[0].split('/').pop()}`);
+ } else { miss++; console.log(` ✗ ${r.mfr_sku}: no image (${url})`); }
+ } catch(e){ miss++; }
+ }
+ } catch(e){ console.error('session err', String(e).slice(0,100)); }
+ finally { if (br) await br.close().catch(()=>{}); }
+ }
+ console.log(`\nLN IMAGE SCRAPE DONE ok=${ok} miss=${miss}`);
+ await db.end();
+})().catch(e=>{console.error('FATAL',e.message);process.exit(1)});
← cd7214e5 Create Phillipe Romano 'Abruzzo Natural Collection' (368 Wal
·
back to Designer Wallcoverings
·
chore: v1.1.6 (session close — Daisy Bennett onboard + contr 6478f4b1 →