← back to Dw Scrape Scheduler
adapters/woo.js
62 lines
// WooCommerce Store API adapter — public, no auth:
// <base>/wp-json/wc/store/v1/products?per_page=100&page=N (fallback: .../store/products)
// $0 local. Store API prices are in MINOR units (cents) — divide by 10^minor_unit.
const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124 Safari/537.36';
async function fetchJson(url, attempts = 4) {
let last = 0;
for (let i = 0; i < attempts; i++) {
const res = await fetch(url, { headers: { 'User-Agent': UA, 'Accept': 'application/json' } });
if (res.ok) return res.json();
last = res.status;
if (i < attempts - 1) await new Promise(r => setTimeout(r, 1500 * (i + 1)));
}
throw new Error(`HTTP ${last} after ${attempts} attempts`);
}
function priceOf(p) {
const pr = p.prices || {};
if (pr.price == null) return null;
const minor = Number(pr.currency_minor_unit ?? 2);
const n = Number(pr.price);
return Number.isFinite(n) ? n / Math.pow(10, minor) : null;
}
async function scrape(base, { maxPages = 60, politeMs = 200 } = {}) {
const root = base.replace(/\/+$/, '');
// Probe which endpoint shape this store exposes.
let path = null;
for (const cand of ['/wp-json/wc/store/v1/products', '/wp-json/wc/store/products']) {
try { const test = await fetchJson(`${root}${cand}?per_page=1`); if (Array.isArray(test)) { path = cand; break; } } catch { /* try next */ }
}
if (!path) throw new Error('no reachable WooCommerce Store API endpoint');
const out = [];
for (let page = 1; page <= maxPages; page++) {
const items = await fetchJson(`${root}${path}?per_page=100&page=${page}`);
if (!Array.isArray(items) || items.length === 0) break;
for (const p of items) {
const img0 = (p.images || [])[0] || {};
out.push({
mfr_sku: p.sku || String(p.id),
handle: p.slug || (p.permalink || '').split('/').filter(Boolean).pop(),
title: p.name,
product_type: (p.type || ''),
vendor: '',
price: priceOf(p),
body_html: p.description || p.short_description || '',
image_url: img0.src || null,
all_images: (p.images || []).map(i => i.src),
product_url: p.permalink || `${root}/?p=${p.id}`,
tags: (p.tags || []).map(t => t.name).filter(Boolean),
raw: p,
});
}
if (items.length < 100) break;
await new Promise(r => setTimeout(r, politeMs));
}
return out;
}
module.exports = { scrape, platform: 'woo' };