#!/usr/bin/env python3 """ audit.py: перевірка SEO та AI-search готовності сторінок стенду. Бере рендер обох версій (before.html та optimized.html), парсить head і JSON-LD, перевіряє обов'язкові поля Product/Offer за вимогами Google Rich Results та пише машиночитний audit.json. Запуск: python3 audit.py [out.json] """ import json import pathlib import re import sys OFFER_REQUIRED = ["price", "priceCurrency", "availability", "url"] PRODUCT_REQUIRED = ["name", "image", "description", "sku", "brand", "offers"] def extract_jsonld(html: str): blocks = re.findall( r'', html, re.S ) parsed = [] for raw in blocks: try: parsed.append(json.loads(raw)) except json.JSONDecodeError as exc: parsed.append({"@type": "PARSE_ERROR", "error": str(exc)}) return parsed def check_page(path: pathlib.Path): html = path.read_text(encoding="utf-8") jsonld = extract_jsonld(html) types = [b.get("@type") for b in jsonld] result = { "file": path.name, "bytes": len(html.encode("utf-8")), "title": (re.search(r"(.*?)", html, re.S) or [None, ""])[1].strip(), "meta_description": bool(re.search(r'' in html, "og_image": bool(re.search(r']*alt=)[^>]*>", html)), "jsonld_types": types, "jsonld_errors": [], } for block in jsonld: if block.get("@type") == "Product": for field in PRODUCT_REQUIRED: if not block.get(field): result["jsonld_errors"].append(f"Product: немає поля {field}") offer = block.get("offers") or {} for field in OFFER_REQUIRED: if not offer.get(field): result["jsonld_errors"].append(f"Offer: немає поля {field}") if block.get("@type") == "PARSE_ERROR": result["jsonld_errors"].append("JSON-LD не парситься: " + block["error"]) result["rich_results_eligible"] = ( "Product" in types and "FAQPage" in types and "BreadcrumbList" in types and not result["jsonld_errors"] ) return result def main() -> None: base = pathlib.Path(sys.argv[1]) if len(sys.argv) > 1 else pathlib.Path(".") out = pathlib.Path(sys.argv[2]) if len(sys.argv) > 2 else base / "audit.json" report = { "stand": "guardlabs.online/demo/shopify-ai-seo", "checked": [ check_page(base / "store" / "before.html"), check_page(base / "store" / "optimized.html"), ], } out.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8") for page in report["checked"]: status = "OK" if page["rich_results_eligible"] else "NO RICH RESULTS" print(f'{page["file"]}: {status}, jsonld={page["jsonld_types"]}, errors={page["jsonld_errors"]}') if __name__ == "__main__": main()