From 242774afd394f6b17142d9aa15f1c25b92bcf002 Mon Sep 17 00:00:00 2001 From: va1is Date: Tue, 5 Aug 2025 13:58:30 +0300 Subject: [PATCH] ZARAHOME prod 1.0 --- ...осто создает экселевские файлики без json.py | 23 ++++ Parsing ZARAHOME/src/xlsx_recorder.py | 112 +++++++++++++++--- 2 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 Parsing ZARAHOME/src/xlsx_recorder просто создает экселевские файлики без json.py diff --git a/Parsing ZARAHOME/src/xlsx_recorder просто создает экселевские файлики без json.py b/Parsing ZARAHOME/src/xlsx_recorder просто создает экселевские файлики без json.py new file mode 100644 index 0000000..5e5f1ad --- /dev/null +++ b/Parsing ZARAHOME/src/xlsx_recorder просто создает экселевские файлики без json.py @@ -0,0 +1,23 @@ +from openpyxl import Workbook + +from os.path import isdir, abspath +from os import mkdir + +# объект для сохранения спаршенных категорий в таблицы +class Recorder: + def __init__(self, records_folder="category_parsed"): + # проверяем наличие папки для сохранения таблиц + if not isdir(abspath('records_folder')): + mkdir(abspath("records_folder")) + self.record_folder = abspath("records_folder") + + # запись и сохранение таблицы + def record(self, csv_name, table_data): + workbook = Workbook() + worksheet = workbook.active + + + for row in table_data: + worksheet.append(row) + + workbook.save(f"{self.record_folder}/{csv_name}.xlsx") \ No newline at end of file diff --git a/Parsing ZARAHOME/src/xlsx_recorder.py b/Parsing ZARAHOME/src/xlsx_recorder.py index 5e5f1ad..59d38e7 100644 --- a/Parsing ZARAHOME/src/xlsx_recorder.py +++ b/Parsing ZARAHOME/src/xlsx_recorder.py @@ -1,23 +1,105 @@ +# xlsx_recorder.py · расширен отправкой JSON from openpyxl import Workbook - -from os.path import isdir, abspath +from os.path import isdir, abspath, join from os import mkdir +import re, json, math, logging, requests, os -# объект для сохранения спаршенных категорий в таблицы +log = logging.getLogger("recorder") + +# ─────────────────────── настройки ─────────────────────── +SEND_JSON = True # отправка POST +SAVE_JSON = True # сохранять копию JSON +POST_URL = "http://localhost:3005/parser/data" + +INVALID_CHARS = r'[<>:"/\\|*?]' +def sanitize_filename(name: str, repl: str = "_") -> str: + """Удаляет/заменяет символы, запрещённые в именах файлов.""" + clean = re.sub(INVALID_CHARS, repl, name) + return clean.split("?", 1)[0].strip() + +# объект для сохранения спаршенных категорий class Recorder: - def __init__(self, records_folder="category_parsed"): - # проверяем наличие папки для сохранения таблиц - if not isdir(abspath('records_folder')): - mkdir(abspath("records_folder")) - self.record_folder = abspath("records_folder") + def __init__(self, records_folder="records_folder"): + # создаём папку при необходимости + rf_abs = abspath(records_folder) + if not isdir(rf_abs): + mkdir(rf_abs) + self.record_folder = rf_abs - # запись и сохранение таблицы - def record(self, csv_name, table_data): - workbook = Workbook() - worksheet = workbook.active + # запись таблицы + JSON/POST + def record(self, csv_name, table): + csv_name = sanitize_filename(csv_name) + # ─── 1) сохраняем XLSX ───────────────────────────── + wb = Workbook() + ws = wb.active + for row in table: + ws.append(row) + xlsx_path = join(self.record_folder, f"{csv_name}.xlsx") + wb.save(xlsx_path) + log.info("XLSX saved → %s", xlsx_path) - for row in table_data: - worksheet.append(row) + # ─── 2) формируем JSON (по утверждённым правилам) ── + headers = table[0] + idx = {h: i for i, h in enumerate(headers)} - workbook.save(f"{self.record_folder}/{csv_name}.xlsx") \ No newline at end of file + items = [] + for row in table[1:]: + # базовые поля + article = row[idx["Артикул"]] + partnumber = row[idx["PartNumber"]] + size_full = row[idx["Свойство: Размер"]].replace("\n", "
") + price_raw = row[idx["Цена закупки"]] + price_int = math.ceil(float(price_raw)) + clr_name_raw = row[idx["Свойство: Цвет"]] + clr_name = clr_name_raw.capitalize() + vis = row[idx["Наличие на сайте"]] + weight_gram = float(row[idx["Свойство: Вес(г)"]]) if row[idx["Свойство: Вес(г)"]] else 0.0 + weight_kg = math.ceil(weight_gram / 1000) if weight_gram else 0 + + # составляем объект + variant = { + "status_id": 1, + "color": clr_name, + "sku": f"{article}-{partnumber}", + "size": size_full, + "cost": price_int, + "originalUrl": row[idx["Краткое описание"]], # url_full в таблице + "originalName": row[idx["Название товара или услуги"]].capitalize(), + "originalDescription": ( + row[idx["Полное описание"]].replace("\n", "
") + "
" + + row[idx["Параметр: Уход"]].replace("\n", "
") + "
" + + row[idx["Параметр: Происхождение"]].replace("\n", "
") + ).strip("
"), + "originalComposition": row[idx["Параметр: Состав"]].replace("\n", "
"), + "images": [img for img in row[idx["Изображения варианта"]].split("\n") if img], + "inStock": vis == "SHOW", + "weight": weight_kg + } + + category_name = re.sub(INVALID_CHARS, "_", + row[idx["Размещение на сайте"]] + .replace("Каталог/ZaraHome/", "")) + items.append({ + "category": {"name": category_name}, + "variant": variant, + "brand": {"name": "zara-home"} + }) + + payload = {"items": items, "parserName": "zara-home"} + + # ─── 3) сохраняем JSON при необходимости ─────────── + if SAVE_JSON: + json_path = join(self.record_folder, f"{csv_name}.json") + with open(json_path, "w", encoding="utf-8") as fh: + json.dump(payload, fh, ensure_ascii=False, indent=2) + log.info("JSON saved → %s", json_path) + + # ─── 4) POST на локальный сервис ─────────────────── + if SEND_JSON: + try: + resp = requests.post(POST_URL, json=payload, timeout=20) + resp.raise_for_status() + log.info("POST %s OK (%s items)", csv_name, len(items)) + except Exception as err: + log.warning("POST %s FAILED: %s", csv_name, err)