# -*- coding: utf-8 -*- """The Greek e-shop technical audit: classify, then measure. Sample frame: Tranco top-1M (list PYG5J, 2026-08-06) filtered to .gr, top N by rank. A site counts as an e-shop if it shows a commerce platform signature, or Product/Offer structured data, or cart wording. All three are recorded so the rule can be varied. """ import collections import io import json import os import statistics HERE = os.path.dirname(os.path.abspath(__file__)) rows = [json.loads(l) for l in io.open(os.path.join(HERE, 'gr_clean.jsonl'), encoding='utf-8') if l.strip()] reached = [r for r in rows if r.get('status') == 200] shops = [r for r in reached if r.get('platform') or r.get('jsonld_product') or r.get('cart_signal')] strict = [r for r in reached if r.get('platform') or r.get('jsonld_product')] def pct(n, d): return (100.0 * n / d) if d else 0.0 print('=' * 92) print('SAMPLE') print('=' * 92) print(' domains attempted %d' % len(rows)) print(' responded HTTP 200 %d (%.0f%%)' % (len(reached), pct(len(reached), len(rows)))) err = collections.Counter((r.get('error') or '').split(':')[0] for r in rows if r.get('status') != 200) print(' failures by kind %s' % dict(err.most_common(6))) print(' classified as e-shops %d (%.0f%% of reachable) [broad rule]' % (len(shops), pct(len(shops), len(reached)))) print(' classified as e-shops %d (%.0f%%) [strict: platform or Product schema]' % (len(strict), pct(len(strict), len(reached)))) S = shops print() print('=' * 92) print('WHAT GREEK E-SHOPS ARE BUILT ON (n=%d)' % len(S)) print('=' * 92) plat = collections.Counter(r.get('platform') or '(none detected)' for r in S) for k, v in plat.most_common(): print(' %-22s %4d %5.1f%%' % (k, v, pct(v, len(S)))) cms = collections.Counter(r.get('cms') or '(none detected)' for r in S) print(' --- underlying CMS ---') for k, v in cms.most_common(6): print(' %-22s %4d %5.1f%%' % (k, v, pct(v, len(S)))) print() print('=' * 92) print('SECURITY HEADERS (share of e-shops sending each)') print('=' * 92) H = [('strict-transport-security', 'HSTS'), ('content-security-policy', 'CSP'), ('x-frame-options', 'X-Frame-Options'), ('x-content-type-options', 'X-Content-Type-Options'), ('referrer-policy', 'Referrer-Policy'), ('permissions-policy', 'Permissions-Policy')] for k, label in H: n = sum(1 for r in S if (r.get('headers') or {}).get(k)) print(' %-26s %4d / %d %5.1f%%' % (label, n, len(S), pct(n, len(S)))) nz = sum(1 for r in S if not any((r.get('headers') or {}).get(k) for k, _ in H)) print(' %-26s %4d / %d %5.1f%%' % ('NONE OF THE SIX', nz, len(S), pct(nz, len(S)))) print() print('=' * 92) print('TRANSPORT') print('=' * 92) tls = collections.Counter(r.get('tls_version') or '(handshake failed)' for r in S) for k, v in tls.most_common(): print(' %-22s %4d %5.1f%%' % (k, v, pct(v, len(S)))) bad = sum(1 for r in S if not r.get('tls_valid')) print(' certificate not valid to a default trust store: %d (%.1f%%)' % (bad, pct(bad, len(S)))) print() print('=' * 92) print('BASELINE HYGIENE') print('=' * 92) for label, f in [('mobile viewport declared', lambda r: r.get('viewport')), ('html lang attribute', lambda r: r.get('lang_attr')), ('exactly one