#!/usr/bin/env python3 """Scrape products + category tree from http://e-shop.kompress.sk (PrestaShop 1.5/1.6). Outputs (next to this script): categories.json - full category tree (flat list with parent_id + path) products.json - one entry per product, with category memberships images// - original (highest quality) product images """ import html import json import os import re import sys import time import urllib.request import urllib.error from concurrent.futures import ThreadPoolExecutor from html.parser import HTMLParser BASE = "http://e-shop.kompress.sk" OUT = os.path.dirname(os.path.abspath(__file__)) IMG_DIR = os.path.join(OUT, "images") UA = "Mozilla/5.0 (X11; Linux x86_64) data-migration-scraper" DELAY = 0.3 # ---------------------------------------------------------------- fetching def fetch(url, binary=False, retries=3): for attempt in range(retries): try: req = urllib.request.Request(url, headers={"User-Agent": UA}) with urllib.request.urlopen(req, timeout=45) as r: data = r.read() return data if binary else data.decode("utf-8", "replace") except urllib.error.HTTPError as e: if e.code == 404: return None if attempt == retries - 1: print(" !! %s -> HTTP %s" % (url, e.code), file=sys.stderr) return None except Exception as e: # noqa: BLE001 - network flakiness if attempt == retries - 1: print(" !! %s -> %s" % (url, e), file=sys.stderr) return None time.sleep(1 + attempt) return None # ---------------------------------------------------------------- helpers TAG_RE = re.compile(r"<[^>]+>") WS_RE = re.compile(r"[ \t\r\f\v]+") def unesc(s): return html.unescape(s or "").strip() def strip_tags(h): """HTML -> readable plain text, keeping block/list structure as newlines.""" if not h: return "" t = re.sub(r"(?i)<\s*br\s*/?>", "\n", h) t = re.sub(r"(?i)", "\n", t) t = re.sub(r"(?i)<\s*li[^>]*>", "• ", t) t = TAG_RE.sub("", t) t = html.unescape(t) t = WS_RE.sub(" ", t) t = re.sub(r" *\n *", "\n", t) t = re.sub(r"\n{3,}", "\n\n", t) return t.strip() def find(pattern, text, group=1, flags=re.S): m = re.search(pattern, text, flags) return m.group(group) if m else None def cat_id_from_url(url): m = re.match(r"^%s/(\d+)-" % re.escape(BASE), url) return int(m.group(1)) if m else None def product_id_from_url(url): m = re.search(r"/(\d+)-[^/]*\.html", url) return int(m.group(1)) if m else None # ---------------------------------------------------------------- categories class TreeParser(HTMLParser): """Parses the nested