← back to Japan Enrich
scrape_gg_sg_brands.py
58 lines
#!/usr/bin/env python3
"""Scrape the distributor brand for the 242 Singapore (goodrichglobal) wallcovering patterns
that came in blank. Singapore markup differs from Thailand: class="psg-single-brands" +
/product_brand/<slug>/ (Thailand was ...-main + /brand/). Updates the distributor field on
those staging rows + the brand map. Parallel headless, $0 local, resumable-ish (idempotent)."""
import json, os, re, subprocess, html as H
from concurrent.futures import ThreadPoolExecutor, as_completed
HERE = os.path.dirname(__file__)
STAGING = os.path.join(HERE, 'staging', 'sangetsu-staging.jsonl')
BRANDS = os.path.join(HERE, 'staging', 'sangetsu-brands.json')
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
# brand link: /singapore/product_brand/<slug>/ ... >Name</a> (class psg-single-brands)
LINK = re.compile(r'/product_brand/([a-z0-9-]+)/"[^>]*class="psg-single-brands"[^>]*>([^<]+)<', re.I)
ALT = re.compile(r'class="psg-single-brands"[^>]*>([^<]+)</a>', re.I)
def brand(url):
try:
h = subprocess.run([CHROME,"--headless=new","--disable-gpu","--no-sandbox","--virtual-time-budget=12000","--dump-dom",url],
capture_output=True, timeout=45, text=True, errors='ignore').stdout
m = LINK.search(h or '')
if m: return url, H.unescape(m.group(2).strip()), m.group(1)
a = ALT.search(h or '')
if a: return url, H.unescape(a.group(1).strip()), None
return url, None, None
except Exception:
return url, None, None
def main():
rows = [json.loads(l) for l in open(STAGING) if l.strip()]
targets = [r['source_url'] for r in rows if r.get('status') == 'gg-sg-new' and not r.get('distributor')]
print(f"scraping brand for {len(targets)} Singapore patterns")
got = {}
with ThreadPoolExecutor(max_workers=6) as ex:
done = 0
for f in as_completed({ex.submit(brand, u): u for u in targets}):
u, dist, slug = f.result()
if dist: got[u] = {"distributor": dist, "slug": slug}
done += 1
if done % 25 == 0: print(f" {done}/{len(targets)} … last={dist}")
# rewrite staging distributor for those rows
for r in rows:
if r['source_url'] in got:
r['distributor'] = got[r['source_url']]['distributor']
with open(STAGING, 'w') as out:
for r in rows: out.write(json.dumps(r, ensure_ascii=False) + '\n')
# merge brand map
bm = json.load(open(BRANDS)) if os.path.exists(BRANDS) else {}
bm.update(got)
json.dump(bm, open(BRANDS, 'w'), ensure_ascii=False, indent=0)
from collections import Counter
c = Counter(v['distributor'] for v in got.values())
print(f"\nresolved {len(got)}/{len(targets)} distributors:")
for k, n in c.most_common(): print(f" {n:4d} {k}")
if __name__ == '__main__':
main()