表头去哪了?110张商品页表格,程序读得出的只有38%
本文目录
- 怎么判断一张表“说清楚了自己”?
- 先看这110张表是从哪来的
- 还原实验的规则
- 四成的表一个th都没有,机器看到的是什么?
- 写对了的那张长什么样
- 行名和列名都写全的表,为什么只有一成?
- 这张表叫什么名字?九成的表答不上来
- 把每一行还原成一条记录,成功率有多少?
- 左上角那个空格子,坑了7张表
- 值本身也会把程序绊倒
- 跨格错位是最难自动修的一类
- 表里那些数字是厘米还是英寸,写在哪儿?
- 公制英制并存的只有一成
- 同一件事,356个表头格子里出现了109种写法?
- 表头里出现最多的词,其实跟尺码没关系?
- 谁写的表更规范?答案跟直觉相反
- 这一层写不写,到底谁在乎?
- 读屏用户
- AI助手与购物代理
- 做数据整合的那个人
- 怎么改,改到什么程度算够?
- 二十行代码,把这套检查跑在自己站上
- 常见问题解答
- 把td改成th,页面样式会不会变?
- scope属性到底要不要写?不写会怎样?
- 尺码表里的单位,写在列名里还是写在每个格子里?
- 合并单元格是不是完全不能用?
- 我的规格是键值对形式,不是二维表,该用什么标签?
- 这一套检查有没有现成工具?
- 权威参考资料
摘要:把55个英文独立站商品页上采到的110张真表格逐张拆开,写一段程序试着把每一行还原成“列名对应值”的记录,能还原的只有38.0%。四成的表一个表头单元格都没有,九成以上的表连个名字都没有,而“胸围”这一个概念在356个表头格子里出现了3种写法,“尺码”这一栏更是有7种。
上一篇数的是载体:那张尺码表在源码里到底是表格、是网格、是图片,还是压根不在。尺码表实测55个站那一篇的结论是,有入口的页面里近一半在DOM里什么都没有。
那剩下那些确实写了table的呢?这一篇接着往里看一层。因为“是一张表格”只是及格线,一张表格真正值钱的地方不在格子里,而在“这一格属于哪一行、哪一列”这层关系上。而这层关系,恰恰是最容易在实现里丢掉的东西——它在屏幕上是免费的,你不写也看得出来。
怎么判断一张表“说清楚了自己”?
样本是同一批:55个站、258个可解析的商品页、110张真表格,来自其中20个站。对每张表,我把最多20行12列的单元格原样取下来,同时记住每个格子是th还是td、有没有scope、有没有caption和thead、有没有跨行跨列。
判据来自规范本身。MDN的th元素说明写得很直白:th不是“加粗居中的td”,它是一个声明——声明这一格是别的格子的标题。而HTML标准里scope属性那一节更进一步,规定了这个标题管的是一整列、一整行,还是一个区块。不写th,那张表在机器眼里就是一堆没有坐标的字符串。
先看这110张表是从哪来的
20个站,110张表,分布极不均匀。baseus一家贡献了11张规格表,monos和reebok各10张,brooklinen 9张,mackweldon 9张,drinkolipop 8张,taylorstitch和everlane各6张。前七家加起来就是63张,占了六成;剩下13个站分掉另外47张,好几个站只有一两张。
这个分布本身说明一件事:写不写表格,是站级决策,不是页级决策。一个站的商品页模板里如果有表格组件,那它每一页都有;没有,那就一页都没有。所以下面所有的比例,与其读成“百分之多少的表怎么样”,不如读成“有表格的这二十家里,多数人在同一个地方犯同一个错”。这跟CSS覆盖率那次实测里看到的形态是一样的——问题几乎全部沉在模板层,不在内容层。
还原实验的规则
还原的目标很低:把第二行开始的每一行,变成一条“列名:值”的记录。判定成功要同时满足四条——首行的格子全是th;表里没有跨行跨列;各行的格子数一致;列名不为空、也不全是纯数字。任何一条不满足,就归到对应的失败类型里。
另外把格子总数不超过2个的表单独摘出来,它们是模板留下的空壳,凑在分母里会把结论做假。110张里这样的有18张,占16.4%,下面的还原成功率都是在剩下92张上算的。
四成的表一个th都没有,机器看到的是什么?
先看最基础的一层。110张表里,一个th都没有的有45张,占40.9%。剩下65张里,首行含th的正好也是65张——换句话说,只要这个站想起来写表头,它就会写在第一行,没有例外。
| 这张表写了什么 | 张数 | 占比 |
|---|---|---|
有thead | 60 | 54.5% |
首行有th(有列名) | 65 | 59.1% |
首列有th(有行名) | 13 | 11.8% |
写了scope | 19 | 17.3% |
有caption | 8 | 7.3% |
| 有跨行跨列 | 18 | 16.4% |
这里有个细节值得停一下:thead有60张、首行th有65张,两个数不一样。也就是说,有几张表写了thead这个分组标签,里面装的却是普通的td。这种写法在视觉上毫无差别——CSS选择器照样能选中它们、照样能加粗,但语义上等于什么都没声明。faherty那几张尺码表就是这样:首行明明白白写着ALPHA、NUMERIC、WAIST (IN)、HIPS (IN),四个格子全是td。
写对了的那张长什么样
光看错的容易丧气,看一张写对的。everlane的裤装尺码表是13行3列,首行三个格子全是th:US Alpha Size、US Numeric Size、Waist;第二行开始是XS、27、27"。三件事它都做到了——列名齐全、列名读得懂、单位跟着值一起写。程序取下来就是一条条干净的记录,读屏软件念到中间那格会先报出列名。
反过来最值得警惕的是allbirds那张。它是整批样本里少数几张同时写了thead和th的表,结构标准得像教科书,内容却是加州隐私法要求披露的信息类别——法务模板带进来的。一个站里写得最规范的那张表,往往不是它自己做的那张。
行名和列名都写全的表,为什么只有一成?
一张尺码表在本质上是二维的:横着是测量部位,竖着是尺码档位,或者反过来。要让机器知道“第4行第3列这个数字是M码的胸围”,光有列名不够,还得有行名。
可样本里首列写了th的只有13张,占11.8%。也就是说,将近九成的表只声明了一个方向。机器读到中间那个数字,最多能知道它属于“胸围”这一列,至于它是哪个码,得靠猜——猜的方式通常是“第一列的那个格子应该是行标识”,这在多数情况下对,但没有任何东西保证它对。
更麻烦的是scope。写了的只有19张,占17.3%;写全到每个th都带的有18张。ARIA的表格角色文档里把列头和行头分成两个不同的角色,就是因为这两件事在语义上不能合并。没有scope的双向表,读屏软件念到某一格时报不出完整坐标,程序解析时也只能退回到位置推断。
这张表叫什么名字?九成的表答不上来
接下来这个数字是全篇里最悬殊的一个:110张表里,既没有caption、也没有aria-label的,有102张,占92.7%。
那这些表叫什么?我写了一段回溯逻辑,沿着DOM往上找最近的标题、图例、按钮或者段落文字,当作它的名字。96张表的名字是这么猜出来的,只有8张来自caption,还有6张连猜都猜不出来。
MDN的caption元素说明里讲的正是这件事:caption是表格的标题,它跟表格是绑死的,不管这张表被搬到页面哪个位置、被复制到哪个上下文里,标题都跟着走。而靠邻近元素猜出来的名字,一旦DOM顺序变了就全错。
猜出来的名字有多不靠谱,看几个真实的:一张6行7列的尺码对照表,最近的那段文字是“CM Inch”,那是单位切换按钮;一张7行2列的表,猜出来的名字是“Fit: Unisex style—true to size”,那是一句文案;还有一张,猜到的是“Transfer History”,而表里唯一的内容是一个句点。
把每一行还原成一条记录,成功率有多少?
92张有实质内容的表跑下来:
| 结果 | 张数 | 占比 | 坏在哪一步 |
|---|---|---|---|
| 还原成功 | 35 | 38.0% | —— |
| 无表头 | 33 | 35.9% | 列名根本不存在 |
| 跨格错位 | 10 | 10.9% | 合并单元格后列位置对不上 |
| 列名里有空格子 | 7 | 7.6% | 左上角那格是空的 |
| 表头行只有一部分是th | 5 | 5.4% | 半声明,比不声明更难处理 |
| 列名本身是数字 | 2 | 2.2% | 列名是尺码值,不是属性名 |
三分之一强能还原。这个数字比我预估的低,也比它看起来更糟——因为“还原成功”只是说程序能把值挂到列名上,没说那个列名读得懂。
左上角那个空格子,坑了7张表
这是最典型的一类。一张标准尺码表,第一行是S、M、L、XL,第一列是胸围、腰围、袖长,那么左上角那一格写什么?多数人的答案是留空。视觉上完全正确,可对程序来说,首行第一个格子是空的,意味着这一列没有名字,整行的对应关系立刻断掉。
规范给的正确写法是把左上角那格也写成th,内容可以是“测量部位”或者干脆写“Size”,再给两个方向的th分别加上scope="col"和scope="row"。这一步的工程成本几乎为零,收益是整张表从“一堆数字”变成“可查询的二维表”。
值本身也会把程序绊倒
还原成功不等于取到的值可用。taylorstitch那张裤装尺码表里,腰围写的是28、30,裤裆长写的是9½,大腿围16½。那个“½”是一个独立的字符,不是9.5。程序按数字解析会得到9,差了半英寸;按字符串存下来,后面做区间比较又用不了。样本里用这种分数字符的表不止一张,服装类尤其常见,因为纸样师傅本来就是这么标的。
还有一类是区间值。faherty的尺码表里,腰围那一列写的是“28-29”“30-31”,胸围那一列写的是“34-36”。这在人看来毫无歧义,程序要用就得先拆成上下界,而拆分符号在同一批样本里就有短横、波浪号、to三种。
这两件事都不算错,页面上写得完全正确。它们只是提醒一件事:表格结构写对了,只是让机器能把值取出来,取出来之后能不能直接用,还得看值本身是怎么写的。想把一批页面的正文原样剥出来对比,可以先用把网页内容剥成干净文本那套流程过一遍,再动手做数值解析。
跨格错位是最难自动修的一类
10张表因为合并单元格而无法还原。taylorstitch那张最典型:整张表12行6列,第一行只有一个格子,写着“Garment Size Chart”——设计上这是个横跨整行的小标题,实现上它是一个colspan为6的单元格。程序按行取格子,第一行取到1个,第二行取到6个,列数对不上,只能判失败。
这类问题不能靠加th解决,得改结构:那行小标题应该是caption,不该混在表体里。
表里那些数字是厘米还是英寸,写在哪儿?
一张尺码表里的数字,脱离单位就没有意义。42可能是厘米,可能是欧码,也可能是胸围英寸。
110张表里,过半格子是纯数字的有18张。这18张里,表内一个单位词都找不到的有3张,剩下15张写了——其中10张写在表头行里,比如“WAIST (IN)”“Chest (in)”,另外几张写在单元格里,比如everlane那张,值直接写成27"。
那3张没写的,单位在哪?全都在页面别处。snowpeak那张6行7列的表最能说明问题:表头是Size、1、S、M、L、XL、XXL,第一列是Shoulder Width、Bust这些部位名,中间全是41、43、45、47.5这样的数字。单位藏在表格上方的一个“CM / Inch”切换按钮里,用户点一下,表里的数字整体换一套。对着屏幕看毫无问题;把这张表原样抓下来,41到底是41厘米还是41英寸,没有任何线索。
公制英制并存的只有一成
| 单位口径 | 张数 | 占比 |
|---|---|---|
| 只出现英制(inch/lb/oz) | 44 | 40.0% |
| 只出现公制(cm/mm/kg/ml) | 3 | 2.7% |
| 两套都出现 | 10 | 9.1% |
英制出现在54张表里,公制只有13张,差了四倍。这批站主要面向北美市场,偏英制不奇怪;奇怪的是同一张表里两套单位都给的只有10张。剩下那些,欧洲和亚洲的买家要么自己算,要么去点那个切换按钮——而切换按钮改的是渲染结果,不是数据本身。这道算术该由谁来做,尺码那一栏的数字不归译者管那篇里已经把整条流水线上的责任缺口拆过一遍。
顺便说一句结构化数据这边的对应物:schema.org的QuantitativeValue把“数值”和“单位代码”定义成两个必须成对出现的字段,正是因为一个脱离单位的数字不构成信息。HTML表格里没有这个约束,所以只能靠人自觉。
同一件事,356个表头格子里出现了109种写法?
把110张表的首行文字全部倒出来,一共356个格子。原样不重复的写法有119种,做过大小写与括号归一之后还剩109种。
然后按概念归拢,结果挺能说明机器这一侧的难处:
- 胸围:3种写法——bust、chest、chest (in);
- 腰围:3种——waist、waist (in)、garment: waist;
- 臀围:3种——hip、hips (in)、garment: low hip;
- 尺码那一栏:7种——size、alpha、alpha size、numeric、numeric size us/ca、us alpha size、us numeric size。
七种写法说的是同一件事:这一行对应哪个码。有的站把字母码和数字码拆成两列,有的合成一列;有的在列名里带上国家,有的不带。任何一个想跨站比较尺码的程序,第一步都得先建一张同义词表,而这张表是没法一劳永逸的——下个季度换个前端组件,写法可能就变了。
相比之下,定义列表那套“名字—值”结构反而更稳,因为它把配对关系写进了标签本身。可惜样本里258页只有43页出现过dl,而且几乎全部来自导航菜单和页脚,没有一个是用来写商品属性的。最适合它的地方没人用,最不适合的地方到处都是。
表头里出现最多的词,其实跟尺码没关系?
把356个表头格子按出现次数排一遍,前几名有点出人意料:
| 表头文字 | 出现次数 | 来自什么表 |
|---|---|---|
| Shipped From | 10 | 配送时效表 |
| Processing Time | 10 | 配送时效表 |
| Shipping Time | 10 | 配送时效表 |
| Unit | 10 | 营养成分表 |
| Height | 10 | 家具尺寸表 |
| Duvet Cover (W x L) | 8 | 床品尺寸表 |
| Waist | 7 | 服装尺码表 |
| S / M / L / XL | 各7 | 服装尺码表 |
排在最前面的三个全是配送信息。这跟上一层的发现能对上:真正写成表格的那些内容里,配送时效和营养成分占了很大比重,而这两类恰好都是有外部约束的——配送时效要写清楚是合规压力,营养成分表在多个市场是法定格式。有人在外面盯着的那些表,写得反而更像表。
另一个细节:Height、Width、Depth这三个词各自只有一种写法,而胸围有3种、尺码那一栏有7种。越是标准化的物理量,写法越统一;越是行业内部的概念,写法越发散。这条规律对做多站点数据整合的人挺有用——先啃发散的那几栏,物理量那几栏基本可以直接对齐。
谁写的表更规范?答案跟直觉相反
这是本篇的对照组。同一批页面上的表格其实来自两拨人:一拨是品牌自己为这件商品做的尺码表、规格表;另一拨是模板、插件、法务文本带进来的——配送时效表、营养成分表、隐私政策里的信息类别表。
按“是不是落在尺码规格语境里”把92张表分成两组,还原成功率是:
| 这张表是谁做的 | 张数 | 能还原 | 成功率 |
|---|---|---|---|
| 品牌自己的尺码表、规格表 | 59 | 28 | 47.5% |
| 模板与法务文本带进来的表 | 33 | 7 | 21.2% |
差了一倍还多。这个结果跟我下场之前的预期正好相反——我原以为品牌自己手搓的尺码表最随意,模板生成的最规矩。实际上反过来:有人真正在乎那张表的内容时,他更可能把表头写出来。模板带进来的那些表没有主人,写成什么样都没人检查,其中还混着那18张只有一两个格子的空壳。
这个对照也顺手回答了另一个问题:这不是“做不到”,是“没人管”。同一个站、同一套前端、同一批工程师,能把尺码表写对,就说明技术上没有障碍。商品页上那个推荐位归谁管那篇里讲的责任真空,在表格这件事上是同一个形态。
这一层写不写,到底谁在乎?
读屏用户
这一侧最直接,也最不容争辩。一张写全了th与scope的表,读屏软件念到某一格会先报出“胸围,M码”,再念数值;一张没写的,念出来就是一串孤零零的数字,用户得靠记忆把它跟表头对上。网站无障碍那18个改动里,表格语义是投入最小的一项之一,也是最容易在改版中被丢掉的一项。语义化HTML那8类标签讲的是同一件事的一般形式。
AI助手与购物代理
用户问“我平时穿M码,这条裤子腰围多少”,模型要么能从这张表里取到那一行,要么去别处找。智能体读的是无障碍树这一点在表格上兑现得特别直接:无障碍树里,一张写了表头的表是有行列坐标的网格,一张没写的表就是一串平级文本。给AI代理做适配时,尺码这一格常常被漏掉,因为它在人眼里已经“显示出来了”。
做数据整合的那个人
如果你要把几十个站的尺码数据拉到一张表里做竞品对比,前面所有的坑会一次性砸下来:四成没有表头,一成的表左上角是空的,109种列名写法,还有半英寸那个字符。尺码标准跟用户自我认知本来就有落差,再叠上一层结构上的噪声,得出的结论很容易是数据处理方式的产物,而不是市场的产物。给自己的尺子配一组对照这件事,在这类活儿里省不掉。
怎么改,改到什么程度算够?
按投入产出排个序,四步:
- 把首行的
td换成th。这是唯一一个改一行代码、语义直接从零变一的动作。前端组件里改一处,全站尺码表一起受益。 - 把左上角那个空格子填上。写成
th,内容写“Size”或者“测量部位”,顺手给两个方向加scope。这一步之后,那张表才真的是二维的。 - 把单位写进列名。不要只留一个切换按钮。最省事的写法是“胸围(cm)”,需要两套就并排给两列。
- 把跨行的小标题挪进
caption。顺带解决了表没有名字这个问题,一举两得。
什么时候可以停?有一个很好用的自检:把整张表复制粘贴到表格软件里,如果每一列都自动落进独立的列、首行就是列名、每个数字都能看出单位,那就够了。粘出来是一坨挤在一列里的文字,说明结构没写对。
还有一件事值得说清楚:把这四步做完,不会直接换来排名。Google早就取消了表格类富媒体结果,schema.org的Table类型也不是Google支持的富媒体类型之一。真正的收益在别处——读屏用户能听懂,AI助手取得到,客服能复制,还有一层往往被忽略:这张表是商品页上信息密度最高的一段,主体内容占比这件事在商品页上本来就吃紧,把它做成可读文本,等于凭空补回一块独有内容。
如果连表格本身都还不存在,那顺序反过来:先解决载体,再谈表头。这一层的实测在同一批样本的载体清点里。
二十行代码,把这套检查跑在自己站上
不用搭实验台。打开一个有尺码表的商品页,先把尺码指南点开,让表真的进到DOM里,然后在浏览器控制台里跑这一段:
[...document.querySelectorAll('table')].forEach((t, i) => {
const trs = [...t.querySelectorAll('tr')];
const lens = trs.map(tr => [...tr.children].length);
const head = trs[0] ? [...trs[0].children] : [];
console.log(i,
'行', trs.length,
'列', Math.max(...lens),
'首行全th', head.length && head.every(c => c.tagName === 'TH'),
'首列th', trs.filter(tr => tr.children[0] && tr.children[0].tagName === 'TH').length,
'scope', t.querySelectorAll('th[scope]').length,
'caption', !!t.querySelector('caption'),
'跨格', t.querySelectorAll('[colspan],[rowspan]').length,
'各行列数一致', new Set(lens).size === 1,
'左上角', head[0] ? JSON.stringify(head[0].textContent.trim()) : '无'
);
});输出里挨个对:首行全th是false,去改第一步;首列th是0而这张表是二维的,去改第二步;左上角打印出来是空字符串,那就是前面说的那7张里的一张;跨格不为0,去看那一格是不是本该当标题用的。
整站铺开的话,把这段接进爬虫,对每个商品页记一行,跑完按站汇总。保哥当时就是这么干的,唯一要多留一步的是:一定要在点开尺码指南之后再跑,不然量到的是另一件事。有些站的表要等点击之后才注入,先跑一遍不点、再跑一遍点开,两个数一对比,顺手就知道自己站属于哪一类,这跟对比爬虫看到的和用户看到的是同一个动作。
常见问题解答
把td改成th,页面样式会不会变?
会变一点。浏览器默认给th加粗并居中,如果你的CSS是按标签选的,改完可能跟设计稿对不上。解决办法是在样式里显式声明font-weight和text-align,一行的事。别为了避开这一行样式改动,把语义丢掉。
scope属性到底要不要写?不写会怎样?
单向表(只有列名)不写通常也能被正确推断,浏览器和辅助技术会按位置猜。双向表必须写,因为“这一格既属于某列也属于某行”这件事没法从位置上稳定推断出来。判据很简单:首列也用了th,就一定要写scope。
尺码表里的单位,写在列名里还是写在每个格子里?
写在列名里。写在每个格子里会让数值变成字符串,程序取出来还得再剥一次单位,也让排序和比较失效。列名写成“Waist (in)”这种形式,格子里只放纯数字,是最省事的做法。
合并单元格是不是完全不能用?
能用,但要用对地方。跨列的表头(比如“上装”横跨三列)是规范支持的,配上scope="colgroup"就没问题。真正该避免的是拿合并单元格做视觉分隔——那种横跨整行的小标题应该用caption或者把表拆成两张。
我的规格是键值对形式,不是二维表,该用什么标签?
用定义列表。dl加dt加dd天生就是一对一的名值结构,比拿两列表格去装更贴切,也不需要写表头。样本里baseus的规格表就是键值对形式硬塞进两列表格的,一个th都没有,还原自然失败。
这一套检查有没有现成工具?
常见的页面体检工具会告诉你“表格缺少表头”,但多半只查th存不存在,不查左上角空格、不查跨格错位、更不查单位。最可靠的还是自己在控制台里跑一段:取出所有表格,逐张打印首行是不是全th、各行格子数是否一致。二十行代码,一次写好可以一直用。
权威参考资料
本文标题:《表头去哪了?110张商品页表格,程序读得出的只有38%》
本文链接:https://zhangwenbao.com/size-chart-column-header-unit-audit.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0