#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""生成《biori.com 产品内容页 SEO/GEO 改进模板》自包含 HTML（默认可编辑可保存）
依据：2026-09-17 实测采集 赛默飞/诺唯赞/翌圣/宝锐 产品页 DOM 与原始 HTML 结构
"""
import os, sys, base64, datetime, html as _html
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from html_editable_layer import apply as apply_edit

ROOT = os.path.expanduser('~/Desktop/Hermes输出-工作类')
OUT = os.path.join(ROOT, '工具/产品页SEO-GEO改进模板_biori_20260917.html')
LOGO = os.path.expanduser('~/work/公司资料/logo_transparent.png')
logo = ('data:image/png;base64,' + base64.b64encode(open(LOGO, 'rb').read()).decode()) if os.path.exists(LOGO) else ''
TODAY = datetime.date.today().isoformat()

# ── 实测对比数据（2026-09-17 采集，raw HTML + 渲染 DOM）──────────
COMP = [
    ("赛默飞 Thermo", "thermofisher.cn/order/catalog/product/18038018", "Taq DNA 聚合酶（野生型）",
     "74 字符：产品名+规格+「Buy Online」+品牌+域名",
     "172 字符，含英文技术定义", "有", "有（locale 化）", "6", "x-default / en-cn / zh-cn",
     "2 块：Product(含 Offer) + BreadcrumbList；渲染后另含 FAQPage、Citations",
     "2/16 缺（12.5%）", "⭐ 标杆"),
    ("诺唯赞 Vazyme", "bio.vazyme.com/product/931.html", "FlysisAmp Cells-to-CT 1-Step Probe Kit",
     "71 字符：产品名_品类_栏目_公司名",
     "103 字符，含卖点与公司名", "有", "有", "5", "无",
     "0 块（无结构化数据）", "9/39 缺（23%）", "内容强/结构化弱"),
    ("翌圣 Yeasen", "yeasen.com/products/detail/1740", "热启动Taq DNA聚合酶(5 U/μL)",
     "⚠️ 渲染后 52 字符正确；但**原始 HTML 抓到的 title 是另一篇文章标题**（需开发核实）",
     "255 字符，长且含上游组分货号", "❌ 无", "❌ 无", "0", "无",
     "0 块", "24/25 缺（96%）", "内容强/技术 SEO 缺"),
    ("宝锐 biori.com（现状）", "biori.com/products?catalogue=LAMP-DNA", "（无产品名）",
     "**12 字符：仅公司名**「珠海宝锐生物科技有限公司」",
     "**14 字符：「珠海宝锐生物科技有限公司官网」**", "❌ 无", "❌ 无", "0", "无",
     "仅 Organization（首页），产品页无 Product", "首页 49/52 缺（94%）", "🔴 待补齐"),
]

GAPS = [
    ("P0", "title 只写公司名", "12 字符，无产品/品类/货号关键词 → 搜索结果里无法被识别为产品页",
     "按「产品名（货号）| 核心指标 | 品类｜BIORI 宝锐生物」公式重写，每页独立；长度 50–60 字符（中文 25–30 字）"),
    ("P0", "无 H1 / 无 H2 语义结构", "标题用 div 拼接，搜索引擎与 AI 无法识别页面主题层级",
     "每页唯一 H1 = 产品全名；H2 = 核心参数 / 应用场景 / 性能数据 / 常见问题 / 文档下载 / 相关产品"),
    ("P0", "无 canonical、无 sitemap", "sitemap.xml 返回 404；多入口（?catalogue=、筛选、行情参数）会产生重复内容",
     "生成 sitemap.xml 并在 robots.txt 加 Sitemap 行；每页加 canonical 指向规范 URL（不带筛选参数）"),
    ("P0", "产品页正文仅 ~1.8K 字符、无参数", "无货号/规格/储存条件/性能数据 → 既没有 SEO 权重，也无法被 AI 引用",
     "每页至少补齐：货号、规格/包装、组分、储存与运输条件、适用平台/样本类型、关键性能数据表"),
    ("P0", "图片 94% 缺 alt", "图片搜索无入口，AI 无法理解图示内容",
     "alt 写「图N-内容说明（产品名 货号）」，实验图必须有图注（可被引用的图注文案）"),
    ("P1", "无结构化数据 Product/FAQ", "失去富媒体摘要（价格/评分/FAQ 折叠），AI 回答时无机器可读来源",
     "注入 JSON-LD：Product(+Offer+Brand+sku) + BreadcrumbList + FAQPage；有操作步骤再加 HowTo"),
    ("P1", "无 og / 无 hreflang", "分享无卡片；中英文站未互指，国际化 SEO 缺失",
     "补 og:title/description/image/url/type；中英对应页互加 hreflang（zh-CN / en / x-default）"),
    ("P1", "产品页无 FAQ / 无文献引用", "竞品均设 FAQ 长文问答 + 引用文献（赛默飞「引用和文献(20)」），是 GEO 最易被引用的素材",
     "每页 5–10 条真实客户/技术支持高频问答（Q 用客户原话，A 给出条件与数值）；列出用本产品的公开文献"),
    ("P2", "无产品间互链（上下架/相关）", "内链权重传递弱，AI 难以把产品组成知识网络",
     "页尾加「同系列 / 上下游配方 / 常搭配产品」，每页 3–6 条内部链接，锚文本含产品名+货号"),
    ("P2", "无「可被引用的事实块」", "生成式引擎倾向于引用数据密度高、条件明确的段落",
     "页首加 TL;DR 事实块：一句话定位 + 3–5 条带数值的事实（如「37 ℃ 7 天 Ct 漂移 ≤1」「−20 ℃ 保存 24 个月」）"),
]

# ── 页面骨架区块（模板正文）────────────────────────────────
BLOCKS = [
    ("1", "H1 产品全名", "**H1（唯一）**：`{产品中文名} {英文名}（货号 {SKU}）`<br>紧接一行**副标题**：目标客户 + 核心收益（≤30 字）",
     "H1 是页面主题的唯一权威声明；AI 抽取时优先取 H1 作为实体名。不要在同页出现第二个 H1。"),
    ("2", "TL;DR 事实块（GEO 关键）", "一句话定位 + 4 条**带数值**的事实：<br>· 适用样本/平台：`__`<br>· 关键性能：`如 37 ℃ 7 天 Ct 漂移 ≤ 1.0，100 拷贝/反应可检出`<br>· 规格与储存：`如 100/1000 T，−20 ℃ 24 个月`<br>· 交付形式：`如 2× 预混液 / 可冻干 DG 版本`",
     "生成式引擎最常引用「条件+数值+单位」齐全的短句。事实块放页首第一个内容区，别藏在图里。"),
    ("3", "核心参数表（机器可读）", "用 **`<table>`**（不要用图片或 div 排版）：<br>货号 / 规格 / 浓度 / 组分 / 适用平台 / 样本类型 / 储存条件 / 运输条件 / 效期 / 是否含 UNG / 是否可冻干",
     "表格是 AI 与搜索引擎最易解析的结构；竞品（赛默飞/诺唯赞）均有参数表，宝锐当前产品页 0 表格。"),
    ("4", "产品描述与原理", "2–4 段：是什么 → 基于什么原理/酶 → 解决什么痛点（用客户语言，如「免提取」「批间差小」）",
     "覆盖长尾问句（「XX 试剂原理」「XX 和 YY 区别」）；每段首句给结论，便于被摘录成摘要。"),
    ("5", "性能数据（带条件）", "数据表或图 + **图注**：`图N 在{平台}{样本}{条件}下的{指标}`，并列出对照竞品/旧版",
     "数据必须说明测试条件，否则 AI 不敢引用；图注是可被引用的文本资产。"),
    ("6", "应用场景 / 推荐体系", "3–6 个场景卡片：`场景 → 推荐货号 → 建议配方/用量`",
     "把「产品」升级为「方案」，命中「怎么做 X」类问句，也是内链枢纽。"),
    ("7", "常见问题 FAQ", "6–10 条，Q 用客户原话、A 给条件与数值；页面可见 + 同步进 FAQPage JSON-LD",
     "FAQ 是 GEO 命中率最高的模块（赛默飞在此有独立 H2，且带 FAQPage 结构化数据）。"),
    ("8", "文档与下载", "说明书 PDF / COA / SDS / 冻干工艺指南，锚文本写清版本与日期",
     "下载类内容提升停留与信任；同时为「说明书」类搜索提供入口。"),
    ("9", "引用文献", "列出使用本产品的公开文献/客户案例（作者、期刊、年份、DOI 或链接）",
     "竞品有「引用和文献(20)」；引用是 AI 判定产品被权威使用的证据。"),
    ("10", "相关产品与配方", "「同系列」「上下游搭配」「常一起采购」各 2–4 条，锚文本含产品名+货号",
     "内链传递权重，帮助 AI 建立产品知识图谱；宝锐当前产品页内链仅 30 条且多为导航。"),
    ("11", "技术声明与更新信息", "页面底部：更新时间、版本、技术支持联系方式（电话/邮箱）",
     "AI 偏好有明确时间与责任主体的页面；也利于 E-E-A-T（专业/权威/可信）评估。"),
]

JSONLD = """<!-- 1. 产品（必填字段标 ★） -->
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "Product",
  "name": "★ HotStart Taq DNA Polymerase（热启动 Taq 聚合酶）",
  "sku": "★ FMD2011",
  "mpn": "FMD2011",
  "brand": { "@type": "Brand", "name": "BIORI 宝锐生物" },
  "manufacturer": { "@type": "Organization", "name": "珠海宝锐生物科技有限公司", "url": "https://www.biori.com" },
  "category": "分子诊断原料 / PCR 酶与预混液",
  "description": "★ 一段说明：适用样本、平台、关键性能（含数值与条件）。",
  "image": ["https://www.biori.com/upload/FMD2011-1.jpg"],
  "url": "★ https://www.biori.com/products/FMD2011",
  "additionalProperty": [
    { "@type": "PropertyValue", "name": "浓度", "value": "5 U/μL" },
    { "@type": "PropertyValue", "name": "储存条件", "value": "-20 ℃" },
    { "@type": "PropertyValue", "name": "效期", "value": "24 个月" },
    { "@type": "PropertyValue", "name": "是否含 UNG", "value": "是" },
    { "@type": "PropertyValue", "name": "是否可冻干", "value": "是（DG 版本）" }
  ],
  "offers": {
    "@type": "Offer",
    "url": "★ https://www.biori.com/products/FMD2011",
    "priceCurrency": "CNY",
    "price": "询价",
    "availability": "https://schema.org/InStock",
    "seller": { "@type": "Organization", "name": "珠海宝锐生物科技有限公司" }
  }
}
</script>

<!-- 2. 面包屑（必填） -->
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "首页", "item": "https://www.biori.com/" },
    { "@type": "ListItem", "position": 2, "name": "产品中心", "item": "https://www.biori.com/products" },
    { "@type": "ListItem", "position": 3, "name": "PCR 酶与预混液", "item": "https://www.biori.com/catalogue/Enzyme-series" },
    { "@type": "ListItem", "position": 4, "name": "HotStart Taq DNA Polymerase（FMD2011）", "item": "https://www.biori.com/products/FMD2011" }
  ]
}
</script>

<!-- 3. 常见问题（GEO 关键，答案 2-4 句 + 数值） -->
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "FAQPage",
  "mainEntity": [
    { "@type": "Question", "name": "这支 Taq 可以用于全血直扩吗？",
      "acceptedAnswer": { "@type": "Answer", "text": "可以。在 XX 项目实测中，配合 XX 缓冲体系可直接扩增全血样本，耐受 XX% 全血，Ct 值与纯化模板差值 ≤1.0（n=20）。" } },
    { "@type": "Question", "name": "冻干前后性能是否一致？",
      "acceptedAnswer": { "@type": "Answer", "text": "DG 冻干版本冻干前后 37 ℃ 加速 7 天的 Ct 漂移 ≤0.5，冻干球 4 ℃ 保存 12 个月性能无显著变化。" } }
  ]
}
</script>

<!-- 4. 操作流程（有步骤时使用，可被 AI 当作「怎么做」的答案） -->
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "HowTo",
  "name": "使用 2×SensiDirect RT Premix 进行一步法 RT-qPCR",
  "totalTime": "PT50M",
  "step": [
    { "@type": "HowToStep", "position": 1, "name": "配制反应体系", "text": "按 20 μL 体系加入 10 μL 2× 预混液、引探与模板。" },
    { "@type": "HowToStep", "position": 2, "name": "上机扩增", "text": "按推荐程序：逆转录 50 ℃ 15 min，预变性 95 ℃ 30 s，45 循环。" }
  ]
}
</script>"""

META_FIELDS = [
    ("<title>", "产品中文名（货号）| 核心指标 | 品类｜BIORI 宝锐生物",
     "≤30 中文字符 / 60 英文字符；货号与品类必须出现；避免全站同一标题",
     "❌ 现状：12 字符仅公司名"),
    ("meta description", "一句话说明适用场景 + 关键性能（带数值）+ 规格 + 行动指引",
     "120–160 字符；写给人看（点击率）也写给 AI 看（摘要素材）；每页唯一",
     "❌ 现状：14 字符「…官网」"),
    ("link rel=canonical", "该产品的规范 URL（不含筛选/排序参数）",
     "解决 ?catalogue= 等参数造成的重复收录；中英文站各自 canonical",
     "❌ 现状：无"),
    ("hreflang", "zh-CN ↔ en 互指，并加 x-default",
     "中英站同一产品的互为翻译标注，避免被判为重复内容",
     "❌ 现状：无"),
    ("og:*", "og:title / og:description / og:image / og:url / og:type=product",
     "飞书/微信/领英分享出卡片，提升点击与品牌一致性",
     "❌ 现状：0 个"),
    ("URL 结构", "/products/{SKU} 或 /products/{slug}-{SKU}",
     "把货号写进 URL，可被直接搜索；避免 /products?catalogue=xx 这类参数 URL 作为产品落地页",
     "⚠️ 现状：产品页走参数 URL"),
    ("图片 alt + 图注", "alt=「图N-内容（产品名 货号）」；正文图注写明条件与指标",
     "图片搜索入口 + AI 理解图示；竞品赛默飞 alt 覆盖率 87.5%，宝锐仅 6%",
     "❌ 现状：94% 缺 alt"),
]

GEO = [
    ("事实密度", "每段尽量含「条件 + 数值 + 单位」，避免「性能优异」这类无信息量形容词",
     "生成式引擎优先引用可验证的量化陈述"),
    ("Q&A 形态", "把客户高频问题原话写成 Q，答案首句给结论、随后给条件与数据",
     "问答对被转述成「直接答案」的概率最高；同时进 FAQPage"),
    ("术语一致", "全站统一货号与产品名写法（同一 SKU 不做 2 种叫法）；中英对照表维护",
     "实体一致性决定 AI 是否把你识别为同一品牌的产品线"),
    ("可引用单位", "统一浓度单位（U/μL）、T 与 μL 的换算口径、Ct 与 ΔCt 的表述方式",
     "口径混乱会让 AI 无法比较，也降低被引用意愿"),
    ("时间与责任", "页面标注「更新时间 / 版本 / 技术支持联系方式」",
     "AI 偏好新近且可追溯来源的内容；E-E-A-T 信号"),
    ("外部信号", "争取第三方页面（论文、客户案例、行业媒体、供应商目录）提到产品名+货号",
     "外部提及是 AI 判断权威度的主要依据之一"),
    ("机器可读", "参数用 table、结构化数据用 JSON-LD（不要只放在图片或 PDF 里）",
     "PDF/图片内容对多数爬虫与 AI 不可解析"),
]

CHECK = [
    ("P0", "每页唯一 H1（产品全名），H2 覆盖参数/场景/性能/FAQ/文档/相关产品"),
    ("P0", "title 按公式重写（含货号与品类），description 120–160 字符且每页唯一"),
    ("P0", "补齐参数表：货号、规格、浓度、组分、储存与运输、效期"),
    ("P0", "生成 sitemap.xml 并在 robots.txt 声明；每页加 canonical"),
    ("P0", "所有图片补 alt；实验图补图注（含条件与指标）"),
    ("P1", "注入 JSON-LD：Product(Offer) + BreadcrumbList + FAQPage（有步骤加 HowTo）"),
    ("P1", "补 og:* 与 hreflang（中英互指 + x-default）"),
    ("P1", "每页 6–10 条 FAQ（客户原话 + 条件数值）并同步 FAQPage"),
    ("P1", "每页列出使用该产品的公开文献/客户案例（≥3 条）"),
    ("P2", "页尾相关产品/上下游互链 3–6 条（锚文本含产品名+货号）"),
    ("P2", "页首 TL;DR 事实块（4 条带数值事实）"),
    ("P2", "统一货号与术语命名规范；建立中英对照表"),
]

CSS = """
*{box-sizing:border-box}
@page{size:A4;margin:12mm 11mm}
body{margin:0;background:#eef2f7;color:#1f2d3d;font:13px/1.7 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif}
.page{max-width:1560px;margin:14px auto;background:#fff;padding:16px 20px 60px;box-shadow:0 2px 14px rgba(16,42,80,.1)}
.hd{display:flex;align-items:center;gap:12px;border-bottom:3px solid #1a73c9;padding-bottom:9px}
.hd img{height:38px}
.hd .nm{font-weight:800;color:#12325b;font-size:15px;letter-spacing:.4px}
.hd .nm small{display:block;font-weight:500;font-size:10.5px;color:#7286a0;letter-spacing:1.5px}
.hd .rt{margin-left:auto;text-align:right;font-size:11.5px;color:#7286a0}
h1{font-size:19px;margin:14px 0 4px;color:#12325b}
.sub{font-size:12.5px;color:#7286a0;margin-bottom:13px}
h2{font-size:15px;color:#12325b;margin:18px 0 8px;padding-left:9px;border-left:4px solid #1a73c9;display:flex;align-items:center;gap:8px}
h2 em{font-style:normal;font-size:11.5px;font-weight:500;color:#8a9bb0;margin-left:auto}
h3{font-size:13.5px;color:#1a5fa8;margin:13px 0 5px}
table{width:100%;border-collapse:collapse;font-size:12.3px;margin-bottom:10px}
th{background:#f2f7fd;color:#12325b;font-weight:600;text-align:left;padding:6px 8px;border:1px solid #dbe6f3}
td{border:1px solid #dbe6f3;padding:6px 8px;vertical-align:top}
td.ok{color:#0d7a47;font-weight:700;text-align:center}
td.no{color:#b52c2c;font-weight:700;text-align:center}
.tag{display:inline-block;border-radius:10px;padding:1px 7px;font-size:11.5px;white-space:nowrap}
.t-p0{background:#fdeaea;color:#b52c2c}.t-p1{background:#fdf0dc;color:#a8641a}.t-p2{background:#e3eefb;color:#1a5fa8}
.t-star{background:#12325b;color:#fff}.t-ok{background:#e2f6ec;color:#0d7a47}
.card{background:#fff;border:1px solid #e3ecf6;border-radius:11px;padding:13px 15px;margin-bottom:13px}
.warn{background:#fff8e6;border:1px solid #f0d080;color:#7a5200;border-radius:8px;padding:9px 12px;font-size:12.4px;margin:8px 0}
.note{background:#eef6ff;border:1px solid #cfe0f2;border-left:4px solid #1a73c9;border-radius:8px;padding:9px 12px;font-size:12.4px;margin:6px 0}
code,pre{font-family:"SF Mono",Menlo,Consolas,monospace}
pre{background:#0f2135;color:#d7e6f5;border-radius:9px;padding:12px 14px;font-size:11.6px;line-height:1.6;overflow:auto;max-height:520px;white-space:pre-wrap}
.bp{border:1px dashed #cfe0f2;border-radius:10px;padding:10px 13px;margin-bottom:9px;background:#fbfdff}
.bp .bt{font-weight:700;color:#12325b;font-size:13.5px;display:flex;align-items:center;gap:8px}
.bp .bt i{display:inline-flex;width:22px;height:22px;border-radius:50%;background:#1a73c9;color:#fff;font-style:normal;font-size:12px;align-items:center;justify-content:center;font-weight:700}
.bp .bd{font-size:12.5px;color:#33506f;margin-top:5px}
.bp .why{font-size:12px;color:#5b7a99;margin-top:5px;padding-left:9px;border-left:2px solid #e3ecf6}
ul.ck{margin:6px 0 0;padding-left:20px;font-size:12.6px}
ul.ck li{margin-bottom:4px}
.foot{font-size:11px;color:#8a9bb0;line-height:1.9;border-top:1px solid #dbe6f3;padding-top:8px;margin-top:14px}
.__EDITCSS__
"""

rows = []
for r in COMP:
    rows.append('<tr><td><b>%s</b></td><td style="font-size:11.5px;word-break:break-all">%s</td>'
                '<td>%s</td><td>%s</td><td>%s</td><td class="%s">%s</td><td class="%s">%s</td>'
                '<td style="text-align:center">%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>'
                % (r[0], r[1], r[2], r[3], r[4],
                   'ok' if r[5] == '有' else 'no', r[5], 'ok' if r[6] == '有' else 'no', r[6],
                   r[7], r[8], r[9], r[10], r[11]))
comp_html = ''.join(rows)

gap_html = ''.join(
    '<tr><td><span class="tag t-%s">%s</span></td><td><b>%s</b></td><td>%s</td><td>%s</td></tr>'
    % (g[0].lower(), g[0], _html.escape(g[1]), _html.escape(g[2]), g[3]) for g in GAPS)

blk_html = ''.join(
    '<div class="bp"><div class="bt"><i>%s</i>%s</div><div class="bd">%s</div><div class="why">为何这样做：%s</div></div>'
    % (b[0], b[1], b[2].replace('**', ''), b[3]) for b in BLOCKS)

meta_html = ''.join('<tr><td><code>%s</code></td><td>%s</td><td>%s</td><td>%s</td></tr>'
                    % (_html.escape(m[0]), m[1], m[2], m[3]) for m in META_FIELDS)
geo_html = ''.join('<tr><td><b>%s</b></td><td>%s</td><td>%s</td></tr>' % g for g in GEO)
chk_html = ''.join('<tr><td><span class="tag t-%s">%s</span></td><td>%s</td><td style="width:60px;text-align:center">☐</td></tr>' % (c[0].lower(), c[0], c[1]) for c in CHECK)

HTML = """<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>biori.com 产品内容页 SEO/GEO 改进模板</title>
<style>__CSS__</style>
<script>__EDITJS__</script></head><body>
<div class="page">
<div class="hd">__LOGO__
<div class="nm">BIORI BIOTECH<small>宝锐生物 · 官网内容规范</small></div>
<div class="rt">制表 __TODAY__<br>用途：biori.com 产品页改版模板</div></div>

<h1>biori.com 产品内容页 SEO / GEO 改进模板</h1>
<div class="sub">依据：__TODAY__ 实测采集 <b>赛默飞 / 诺唯赞 / 翌圣 / 宝锐</b> 产品页的原始 HTML 与渲染后 DOM（含 title、description、canonical、og、hreflang、JSON-LD、H 标签、表格、图片 alt 覆盖率）</div>

<div class="note"><b>一句话结论</b>：竞品把产品页做成「<b>参数齐全 + 结构化数据 + FAQ + 文献</b>」的可被机器引用的资产；宝锐产品页目前是「<b>仅公司名的标题 + 无 H1 + 无结构化数据 + 94% 图片无 alt + 正文 1.8K 字符</b>」，既拿不到搜索排名，也不会被 AI 引用。本模板给出可直接落地的页面骨架、字段规范与 JSON-LD 代码。</div>

<h2>① 实测对比（四家产品页技术结构）<em>原始数据</em></h2>
<table>
<tr><th style="width:9%">站点</th><th style="width:11%">样本页</th><th style="width:11%">产品</th><th style="width:14%">title</th><th style="width:10%">description</th>
<th style="width:5%">H1</th><th style="width:6%">canonical</th><th style="width:4%">og</th><th style="width:8%">hreflang</th><th style="width:16%">JSON-LD</th><th style="width:9%">图片 alt 缺失</th><th>评价</th></tr>
__COMP__
</table>
<div class="warn">⚠️ 翌圣一项需开发核实：同一 URL 的**原始 HTML 里 title 是另一篇文章标题**，而浏览器渲染后是正确的产品标题——说明标题可能由 JS 注入，爬虫抓到的可能不是产品标题。</div>

<h2>② 宝锐现状差距清单 <em>P0/P1/P2</em></h2>
<table>
<tr><th style="width:6%">级别</th><th style="width:20%">问题</th><th style="width:32%">实测证据</th><th>改进要求</th></tr>
__GAPS__
</table>

<h2>③ 产品内容页模板骨架（11 个区块，按此顺序） <em>可直接给开发/CMS</em></h2>
__BLOCKS__
<div class="note"><b>区块顺序原则</b>：先给机器和用户「结论与事实」（1–3），再给「解释与数据」（4–6），最后给「信任与延伸」（7–11）。<b>事实在前，叙事在后</b>——这是 SEO 与 GEO 共用的排序逻辑。</div>

<h2>④ SEO 字段填写规范 <em>每个字段都有公式</em></h2>
<table>
<tr><th style="width:16%">字段</th><th style="width:30%">填写公式 / 模板</th><th style="width:30%">规则</th><th>宝锐现状</th></tr>
__META__
</table>

<h2>⑤ GEO（生成式引擎优化）专项规范 <em>让 AI 愿意引用你</em></h2>
<div class="sub" style="margin:0 0 8px">GEO ≠ 传统 SEO：目标不是排名，而是在 ChatGPT / Perplexity / 各家 AI 搜索回答里**被当作答案来源引用**。核心是「可验证、可摘录、可归因」。</div>
<table>
<tr><th style="width:16%">要点</th><th style="width:46%">做法</th><th>原理</th></tr>
__GEO__
</table>

<h2>⑥ 结构化数据代码（可直接复制） <em>JSON-LD · 4 类</em></h2>
<div class="note">把下面 4 段放进产品页 <code>&lt;head&gt;</code> 或 <code>&lt;body&gt;</code> 末尾；把 <code>&lt;script&gt;</code> 全部复制，按注释替换占位内容。★ = 必填字段。<b>FAQPage 的问答必须与页面可见内容一致</b>（不一致会被判违规）。</div>
<pre>__JSONLD__</pre>

<h2>⑦ 落地实施清单（可勾选）</h2>
<table>
<tr><th style="width:6%">级别</th><th>检查项</th><th style="width:60px">完成</th></tr>
__CHECK__
</table>

<h2>⑧ 技术侧配套（一次性）</h2>
<ul class="ck">
<li><b>sitemap.xml</b>：当前 <code>https://www.biori.com/sitemap.xml</code> 返回 <b>404</b>；先生成含全部产品/文章/分类页的 sitemap，并在 <code>robots.txt</code> 追加 <code>Sitemap: https://www.biori.com/sitemap.xml</code>（现有 robots.txt 仅两行，未声明 sitemap）。</li>
<li><b>URL 规范</b>：产品落地页从 <code>/products?catalogue=LAMP-DNA</code> 改为 <code>/products/{SKU}</code>（或 <code>/products/{slug}-{SKU}</code>），旧参数 URL 301 到新 URL。</li>
<li><b>canonical + hreflang</b>：中英文站每个产品页互指，并加 <code>x-default</code>；避免两站被判重复内容。</li>
<li><b>结构化数据自测</b>：上线后用 Google Rich Results Test / Schema 校验器跑一遍，确保 FAQPage 与可见内容一致、Product 有 image/offers。</li>
<li><b>性能与可抓取</b>：产品页正文不要靠 JS 后置渲染（翌圣的教训）；关键内容（H1/参数表/FAQ）必须在原始 HTML 里。</li>
<li><b>数据统一</b>：产品页参数与货号建议直接读宝锐已有的产品主数据（工作台 <code>数据/product_master.json</code> 同一套口径），避免官网与销售口径不一致。</li>
</ul>

<div class="foot">
<b>实测方法与时间</b>：__TODAY__ 用真实浏览器（渲染后 DOM）+ 原始 HTML 双通道采集；指标含 title/description 字符数、H1/H2/H3 计数、canonical、og 标签数、hreflang、JSON-LD 块与 @type、table 数、图片 alt 缺失率、robotts/sitemap 可用性。<br>
<b>样本限制</b>：每站仅取 1 个代表性产品页（赛默飞 18038018、诺唯赞 product/931、翌圣 detail/1740、宝锐 LAMP-DNA 列表页），不代表全站平均水平；如需全站普查可扩展为批量抓取脚本。<br>
本页所有数字均来自实测，未做估算。页面文字均可直接编辑，改动自动保存在本机浏览器；「⬇ 导出 HTML」生成带数据的独立文件。
</div>
</div>
__TOOLBAR__
</body></html>"""

html = (HTML.replace('__CSS__', CSS)
        .replace('__LOGO__', ('<img src="%s" alt="BIORI">' % logo) if logo else '')
        .replace('__TODAY__', TODAY)
        .replace('__COMP__', comp_html).replace('__GAPS__', gap_html)
        .replace('__BLOCKS__', blk_html).replace('__META__', meta_html)
        .replace('__GEO__', geo_html).replace('__JSONLD__', __import__('html').escape(JSONLD))
        .replace('__CHECK__', chk_html))
import re as _re
html = _re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', html)
html = apply_edit(html, ls_key='baoyu-seo-geo-template-20260917',
                  file_base='产品页SEO-GEO改进模板_biori',
                  hint_anchor='<h1>biori.com')
open(OUT, 'w', encoding='utf-8').write(html)
print('✅ %s  %.1f KB' % (OUT, os.path.getsize(OUT) / 1024))
print('   对比行 %d ｜ 差距 %d ｜ 区块 %d ｜ 字段 %d ｜ GEO %d ｜ 清单 %d'
      % (len(COMP), len(GAPS), len(BLOCKS), len(META_FIELDS), len(GEO), len(CHECK)))
print('   宏残留:', [x for x in ['__CSS__', '__EDITJS__', '__COMP__', '__GAPS__', '__BLOCKS__', '__META__', '__GEO__', '__JSONLD__', '__CHECK__', '__TOOLBAR__', '__LOGO__', '__TODAY__'] if x in html])
