← back to Designerwallcoverings
scripts/artmura-onboard/fill_specs.py
63 lines
#!/usr/bin/env python3
"""Gently fill missing PDP spec fields in data/artmura.json (sequential, backoff-aware)."""
import json, os, re, time, urllib.request
HERE = os.path.dirname(os.path.abspath(__file__))
PKG = os.path.join(HERE, "data", "artmura.json")
UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
def fetch(url):
for i in range(5):
try:
req = urllib.request.Request(url, headers={"User-Agent": UA})
with urllib.request.urlopen(req, timeout=30) as r:
return r.read().decode("utf-8", "replace")
except urllib.error.HTTPError as e:
if e.code == 429:
wait = 20 * (i + 1)
print(f" 429 — backing off {wait}s")
time.sleep(wait); continue
raise
except Exception:
time.sleep(5)
return None
def parse_specs(h):
pairs = re.findall(r'product-info__label">([^<]+)</th>\s*<td class="product-info__value">(.*?)</td>', h, re.S)
out, seen = {}, set()
for lbl, val in pairs:
lbl = lbl.strip().rstrip(":")
if lbl in seen: continue
seen.add(lbl)
out[lbl] = re.sub(r"<[^>]+>", "", val).strip()
return out
def width_inches(dim):
m = re.search(r'([\d.]+)\s*"', dim or "")
return float(m.group(1)) if m else None
d = json.load(open(PKG))
todo = [r for r in d["products"] if not r["dimensions"]]
print(f"filling {len(todo)} products with missing specs ...")
filled = 0
for n, r in enumerate(todo, 1):
h = fetch(r["product_url"])
if not h:
print(f" [{n}/{len(todo)}] {r['mfr_sku']} FAILED"); continue
s = parse_specs(h)
if s.get("Dimensions"):
r["substrate"] = r["substrate"] or s.get("Substrate available")
r["grade"] = s.get("Grade available")
r["dimensions"] = s.get("Dimensions")
r["width_inches"] = width_inches(s.get("Dimensions"))
r["wall_coverage"] = s.get("Wall coverage")
r["lead_time"] = s.get("Lead-time")
r["origin"] = s.get("Origin")
filled += 1
print(f" [{n}/{len(todo)}] {r['mfr_sku']} ✓ {r['dimensions']} · {r['origin']}")
else:
print(f" [{n}/{len(todo)}] {r['mfr_sku']} no spec table")
json.dump(d, open(PKG, "w"), indent=2) # checkpoint each
time.sleep(1.2)
print(f"done. filled {filled}/{len(todo)}. total w/dims now {sum(1 for r in d['products'] if r['dimensions'])}/161")