一个很常见的现场:页面正文写着「价格按配置与数量报价,请提交询价」,源码里却是主题示例没删干净的 "price": "0.00"。搜索结果里出现 ¥0 的商品卡片,或者 AI 摘要把「起价 0 元」直接念出来。
这类错误不报错、不崩站,只在有人看的时候丢人。下面是一套从判断页型到上线验证的流程。
Offer.price 的语义是「这个价格能买到」。填 0 表示免费,不表示面议。占位写法("price": "0"、"price": "1"、只写 priceCurrency 不写 price)要么被当成真实价格读出去,要么直接被判为无效。
这里有个代价要接受:商品富媒体结果一般要求页面有 name,再加上 offers 或真实评价中的至少一项。一个既没有公开价格、也没有真实评价的产品页,通常拿不到带价格的展示卡片。为了拿到卡片去编价格、编评分,属于明确违反结构化数据政策的做法,被人工处理比没有卡片糟得多。
同时不要:
aggregateRatingavailability 填 InStock,实际要报价后才知道能不能排产offers.url,让人以为点进去就能买priceValidUntil 写一个已经过去的日期没有价格可写,就把「这是什么」写扎实。这些字段搜索引擎会读,AI 摘要也更容易引到具体参数:
name、description,描述里带上「价格按配置与数量报价」这句事实image,多张,绝对地址brand、category、materialsku / mpn / gtin,只在系统里真实存在时写,不要为了「更完整」编一个additionalProperty,把参数拆成 PropertyValue 并带单位isSimilarTo、isVariantOf 这类关系字段,页面上真有对应关系时再用一个「价格面议」的工业泵页面可以这样写:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "304 不锈钢离心泵 CB-25",
"sku": "CB-25",
"mpn": "CB-25-SS",
"image": [
"https://example.com/img/cb-25-1.jpg",
"https://example.com/img/cb-25-2.jpg"
],
"description": "304 不锈钢卧式离心泵,流量 25 m³/h,扬程 32 m。价格按配置与订购数量报价,最小起订量 10 台。",
"brand": { "@type": "Brand", "name": "Example Pumps" },
"category": "工业泵/离心泵",
"material": "304 不锈钢",
"additionalProperty": [
{ "@type": "PropertyValue", "name": "流量", "value": 25, "unitCode": "MQH" },
{ "@type": "PropertyValue", "name": "扬程", "value": 32, "unitCode": "MTR" },
{ "@type": "PropertyValue", "name": "进口口径", "value": 65, "unitCode": "MMT" },
{ "@type": "PropertyValue", "name": "最小起订量", "value": 10, "unitCode": "C62" }
]
}注意这段里没有 offers。unitCode 用 UN/CEFACT 代码:MQH 是立方米每小时,MTR 是米,MMT 是毫米,C62 是「个」。拿不准就改用 "unitText": "m³/h",别硬凑代码。
结构化数据必须和可见内容一致。正文里要能一眼找到这几句,AI 拿不到结构化数据里的价格时,就会来找这些句子:
这几行本来也是询价表单该问的字段,写出来不算额外工作量。
询价本质是一次联系行为。表单页用 ContactPage(或 WebPage 加 mainEntity)描述就够了,不需要给 Product 挂一个自定义 action,指望搜索引擎识别成「询价按钮」——schema.org 里没有这个语义,挂了也不会被当成可执行动作。
产品页上如果确实有可执行的功能(比如按型号查替代料的查询框),那描述这个页面本身更准确,用 WebPage 就够了。
最常见的来源是主题或插件里的示例 JSON-LD 没删。生成时加一道判断:后台没填真实价格,就不输出 offers。
add_action('wp_head', function () {
if (!is_singular('product')) {
return;
}
$id = get_queried_object_id();
$data = [
'@context' => 'https://schema.org',
'@type' => 'Product',
'name' => get_the_title($id),
'description' => wp_strip_all_tags(get_the_excerpt($id)),
'image' => array_values(array_filter([
get_the_post_thumbnail_url($id, 'full'),
])),
'sku' => get_post_meta($id, '_sku', true),
'brand' => [
'@type' => 'Brand',
'name' => get_post_meta($id, '_brand', true),
],
'additionalProperty' => array_map(
fn($row) => [
'@type' => 'PropertyValue',
'name' => $row['name'],
'value' => $row['value'],
'unitText' => $row['unit'],
],
(array) get_post_meta($id, '_specs', true)
),
];
// 只有后台确实填了价格才写 offers
$price = get_post_meta($id, '_price', true);
if (is_numeric($price) && (float) $price > 0) {
$data['offers'] = [
'@type' => 'Offer',
'price' => (string) $price,
'priceCurrency' => get_post_meta($id, '_currency', true) ?: 'CNY',
'availability' => 'https://schema.org/InStock',
'url' => get_permalink($id),
];
}
$clean = array_filter($data, static function ($v) {
return $v !== null && $v !== '' && $v !== [];
});
printf(
'<script type="application/ld+json">%s</script>' . PHP_EOL,
wp_json_encode($clean, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
);
}, 20);_specs 为空时 additionalProperty 会被 array_filter 去掉,这是对的;但如果你希望没有参数的产品页也保留这个字段,就别让它进过滤。
抓一份渲染后的 HTML,看有几个 JSON-LD 块、里面有没有价格和评分:
curl -s "https://example.com/products/cb-25/" -o page.html
python3 - <<'PY'
import re, json
html = open('page.html', encoding='utf-8').read()
blocks = re.findall(r'<script[^>]+application/ld\+json[^>]*>(.*?)</script>', html, re.S)
print(len(blocks), 'JSON-LD blocks')
for b in blocks:
d = json.loads(b)
print(d.get('@type'), '| offers:', 'offers' in d, '| rating:', 'aggregateRating' in d)
PY要看到的是:只有一个 Product 块(主题一份、插件一份是最常见的重复),offers: False,rating: False。多出来的块要么来自主题示例,要么来自某个默认开着的开关,去对应位置关掉。
改完上线,在 Search Console 的增强功能里看商品相关报告有没有新增错误。没有富媒体展示不算错,是这套配置的正常结果。
定价策略后来变了(比如决定公开起价),回头改 JSON-LD 这一步最容易忘,把它写进改价清单里一起做。结构化数据这块我们做成了免费插件 AumViso,schema、llms.txt、sitemap、meta 一起输出:https://aumcreate.com/plugins/aumviso?utm_source=tencent&utm_medium=content&utm_campaign=tencent-product-schema-no-price-inquiry。用不用插件都行,关键是「没有价格就不输出 offers」这条规则要落到生成逻辑里,而不是靠人记得。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。