本地商家站补 LocalBusiness,先拆掉自评星标
一句话结论:本地服务站的 JSON-LD 大多不是没有,而是打错了类型又硬塞自评评分——按 "最具体子类型 + 两个必填 + 营业时间格式" 三处修一遍,20 分钟能落地,且不会因为 aggregateRating 让整块标记失去星标资格。
背景:首页里那两块 JSON-LD,通常不是 LocalBusiness
很多本地商家站的标记来源不是人写的,而是主题或插件几年前自动输出的 Organization 块——电话是旧的,logo 已经 404,地址停在搬家前。
2026-11-11 本机对 4 个美国连锁本地服务品牌首页做了实测(直接请求原始 HTML,正则抽 application/ld+json 块,逐块 JSON.parse):
| 站点 | HTTP | HTML 字节 | JSON-LD 块数 | 解析错误 | 出现的 @type | openingHoursSpecification | aggregateRating |
|---|---|---|---|---|---|---|---|
| jiffylube.com | 200 | 97,782 | 2 | 0 | Organization, WebSite | 0 条 | 0 个 |
| midas.com | 200 | 453,183 | 2 | 0 | Organization, WebPage | 0 条 | 0 个 |
| aspendental.com | 403(站点侧拦截) | — | — | — | — | — | — |
| mrrooter.com | 403(站点侧拦截) | — | — | — | — | — | — |
可抓的两站合计 4 块 JSON-LD,LocalBusiness 及其任何子类型出现 0 次,营业时间规格 0 条,评分 0 个。标记"合法且能解析",但 Google 从里面读不到"这是一家什么店、在哪、几点开门"。这就是本地商家站最常见的一种沉默失败:验证器不报错,实体却没被描述。
落地清单一:先把类型选对,再谈字段
Google 的 LocalBusiness 文档要求用最贴合的具体子类型,LocalBusiness 只是兜底。schema.org 的层级大致是:
LocalBusiness ├── HomeAndConstructionBusiness → Plumber / Electrician / RoofingContractor ├── AutomotiveBusiness → AutoRepair / AutoWash / AutoDealer ├── FoodEstablishment → Restaurant / CafeOrCoffeeShop / Bakery ├── MedicalBusiness → Dentist / Optician / Physiotherapy ├── HealthAndBeautyBusiness → HairSalon / NailSalon / DaySpa └── Store / ProfessionalService / EntertainmentBusiness ...
三条判断规则:
能落到叶子就落叶子。水管工写
Plumber,不要写LocalBusiness;牙科写Dentist。一个商家确实是两件事时用类型数组,Google 明确接受:
"@type": ["Plumber", "HVACBusiness"]。不要用
additionalType补充业务类别——Google 的文档写明不支持,写了等于没写。
落地清单二:必填只有两个,但推荐字段有格式硬要求
按 Google LocalBusiness 文档口径(查询于 2026-11-11):必填仅 name 与 address,其余为推荐。但几个推荐字段带格式规则,写错不会报错、只会被忽略:
| 字段 | 格式要求 | 反例 |
|---|---|---|
telephone | 主号码,含国家码与区号 | (512) 555-0142 |
geo | 经纬度,至少 5 位小数 | 30.26, -97.74(城市中心点) |
priceRange | 少于 100 字符 | 一整句"免费上门估价,价格透明" |
url | 该门店自身的完整 URL,且必须能访问 | 跳到品牌首页 |
sameAs | 商家自己拥有的资料页 | 猜出来的点评页地址 |
image | 可访问的门店/车辆/团队图 | 404 的 logo |
一份可以直接抄的完整块(示例主体为虚构水管公司,域名与电话均使用保留示例值,落地时全部替换为真实且页面上可见的信息):
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Plumber",
"@id": "https://harborlaneplumbing.example/#business",
"name": "Harbor Lane Plumbing",
"url": "https://harborlaneplumbing.example/",
"telephone": "+1-512-555-0142",
"image": "https://harborlaneplumbing.example/img/van.jpg",
"priceRange": "$$",
"address": {
"@type": "PostalAddress",
"streetAddress": "100 Example Ave, Suite 2",
"addressLocality": "Austin",
"addressRegion": "TX",
"postalCode": "78701",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 30.26715,
"longitude": -97.74306
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "07:00:00",
"closes": "19:00:00"
},
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": "Saturday",
"opens": "08:00:00",
"closes": "14:00:00"
}
],
"sameAs": ["https://www.facebook.com/example-page"]
}
</script>@id 不花钱,但值钱:后面在服务页、文章页引用这家公司时,直接 {"@id": "https://...#business"} 指回去,实体不会被拆成两个人。
落地清单三:营业时间,格式比内容更容易错
营业时间是这块标记里最容易长期漂移的部分:门店改了周六营业时间,页脚更新了,JSON-LD 三年没动。Google 的格式规则:
| 情形 | 写法 |
|---|---|
| 常规时段 | opens / closes 用 hh:mm:ss,dayOfWeek 写 "Monday" 或数组 |
| 24 小时营业 | "opens": "00:00", "closes": "23:59" |
| 全天休息 | "opens": "00:00", "closes": "00:00" |
| 跨午夜(如周五 18:00 到次日 02:00) | 单独一条,18:00:00 → 02:00:00 |
| 季节性 / 节假日 | 追加 validFrom / validThrough,格式 YYYY-MM-DD |
| 全年不变 | 省略 validFrom / validThrough |
节假日闭店叠加在常规时段之上,像这样:
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": "Thursday",
"opens": "00:00:00",
"closes": "00:00:00",
"validFrom": "2026-11-26",
"validThrough": "2026-11-26"
}如果站点是 CMS,让营业时间来自同一份数据源——页脚渲染用什么,JSON-LD 就用什么。这一处改动能挡掉绝大多数"标记与页面不一致"。静态站至少把两者放进同一个 partial,让改动的人一眼看到另一处。
落地清单四:先看页面上已经有什么,再动手加
插件、主题、标签管理器都会往页面里注入 JSON-LD。动手前先跑一遍控制台,把所有块和它们的 @type 列出来:
[...document.querySelectorAll('script[type="application/ld+json"]')]
.map((s, i) => {
try {
const data = JSON.parse(s.textContent);
const items = [].concat(data['@graph'] || data);
return { block: i, types: items.map(x => x['@type']) };
} catch (e) {
return { block: i, error: e.message };
}
});如果出现两个 LocalBusiness 类型块、电话还不一样,先解决冲突,别急着加第三块。上面 4 站实测里 jiffylube 与 midas 都是 2 块,各由不同来源注入——这正是冲突的高发形态。
服务端批量核验同理,本机用 Python 标准库就能跑(正则抽块 → JSON.parse → 统计类型与字段):
import re, json, urllib.request
def audit(url):
html = urllib.request.urlopen(
urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}), timeout=20
).read().decode('utf-8', 'ignore')
blocks = re.findall(r'<script[^>]*type=["\']application/ld\+json["\'][^>]*>(.*?)</script>', html, re.S | re.I)
types, errors, hours = [], 0, 0
for b in blocks:
try:
d = json.loads(b.strip())
except Exception:
errors += 1
continue
for it in (d if isinstance(d, list) else [d]):
t = it.get('@type')
types += t if isinstance(t, list) else [t]
hours += len(it.get('openingHoursSpecification') or [])
return {'blocks': len(blocks), 'parseErr': errors, 'types': sorted(set(filter(None, types))), 'hours': hours}
print(audit('https://www.jiffylube.com/'))落地清单五:三道验证
Rich Results Test(search.google.com/test/rich-results):贴线上 URL 或代码,errors 必修,warnings 通常是缺推荐字段。
Schema Markup Validator(validator.schema.org):按完整 schema.org 词汇表校验,能抓到
adress这类拼写错误——只面向 Google 的工具会直接忽略它。Search Console 的网址检查:确认页面可被抓取,没被 robots.txt 或
noindex挡住。Google 明说重新抓取可能要几天,别承诺"周五前生效"。
结果:没有数字,有三处可观察的变化
按 Google 自己的说明,结构化数据"不保证会出现在搜索结果里",它解决的是"让机器别把你的信息读错",不是排名开关。本次落地没有 CTR、曝光、排名数据可报,能确认的是三件事:
自评星标不再占位:很多商家站把 Google 评分硬编码进
aggregateRating想换星标。按 Google 评论摘录规范,如果被评论的实体自己控制了这些评论(商家在自己的站上标自己的评分),其页面用LocalBusiness或任何 Organization 类型都不具备星标资格。也就是说这块标记不会出星,只增加不一致风险——直接删掉,评论给人看。重复块消失:插件与手写块并存时电话、地址会打架;合并成单一实体(统一
@id)后,"哪个地址是对的"这类人工核对不再发生。营业时间不再漂移:用同一数据源渲染页脚与 JSON-LD 后,"页面写着周六开门、标记里还是旧的"这类不一致不再出现。
本机 4 站抽样给出的定性判断是:本地服务站的标记问题不是缺代码,而是缺实体——4 块标记里 0 个 LocalBusiness 子类型,等于把最该说清楚的事(这是什么业务、在哪、几点开)留空了。
复盘:3 条动作 + 2 条红线
该做的三件事:
类型落到最具体的子类型(
Plumber、Dentist、AutoRepair),确有两项业务才用类型数组;不要用additionalType补类别。name+address之外,优先补齐telephone(含国家码)、geo(≥5 位小数)、priceRange(<100 字符)、url(门店页本身)——这四项是格式最容易写错又最容易被忽略的。营业时间与页面共用一份数据源,跨午夜/节假日按
validFrom+validThrough单条表达,别用"每天 24 小时"蒙过去。
两条红线(别这么做):
别给自己打
aggregateRating:商家在自家站标自家评分,Google 评论摘录规则下不具备星标资格,属于"标了也没用,还可能被判定不一致"。别标记用户看不见的内容:营业时间、服务区域、价格区间没在页面上展示,就不要写进 JSON-LD。Google 通用结构化数据指南明确要求"不要标记页面读者看不到的内容"。
标注
@type:
LocalBusiness(含子类型Plumber、Dentist、AutoRepair等);配套PostalAddress、GeoCoordinates、OpeningHoursSpecification。Schema.org 版本:现行版,查询于 2026-11-11(当日
schema.org/version/latest连接超时,未取到具体版本号;最近已知版本为 v30.1,2026-09-16 发布)。验证工具与时间:Google Rich Results Test、Schema Markup Validator(validator.schema.org)、Search Console 网址检查;本机 Python 标准库抽样脚本运行于 2026-11-11。
数据口径:4 站抽样的状态码、字节数、块数、字段计数为 2026-11-11 本机直接请求原始 HTML 得到,可复跑;结构化数据"不保证出现在搜索结果"、LocalBusiness 必填与推荐字段格式规则、评论摘录的自评资格限制,均取自 Google Search Central 公开文档口径。全文不含 CTR、曝光、排名、AI 引用变化数据,效果为结构性观察。
来源:https://dev.to/leadreef_ai/localbusiness-structured-data-the-20-minute-fix-most-small-business-sites-are-missing-1cng (案例与字段写法);Google Search Central《Local business (LocalBusiness) structured data》《Review snippet》《General structured data guidelines》;schema.org
Plumber、Dentist类型页。标签:Schema.org、结构化数据、JSON-LD、AI可见度、LocalBusiness