推荐系统的效果上限往往取决于输入数据的质量。除了常见的埋点日志,网页本身的结构化内容也是极易获取却常被忽视的数据来源。借助对HTML文档的解析,我们可以把页面上的标题、分类、价格、评论摘要等信息转成算法可消费的特征,从而为基于内容的推荐或混合推荐提供底层支撑。

一、为什么HTML数据适合作为推荐算法的数据基础
HTML是绝大多数网站内容的载体,它天然带有层级结构和语义标签。相比于纯文本或二进制文件,HTML通过标签把不同性质的信息做了物理隔离,例如商品名一般出现在<h1>中,价格常在带有特定class的<span>里,分类路径多由面包屑导航的<a>链接组成。这种结构让机器可以用较低成本抽取出稳定字段。
从推荐系统角度看,协同过滤类算法在物品冷启动阶段表现很差,因为一个新上架商品还没有任何交互记录。此时如果直接从HTML里拿到它的类目、品牌、规格说明,就能用基于内容相似度的方式先做出初步推荐。也就是说,HTML数据填补了行为数据的时间空白,让推荐模型在零交互时也有据可依。
1.1 静态特征与动态行为的互补
用户行为数据属于动态特征,随时间波动大,且存在稀疏问题。HTML解析出的内容特征则是相对静态的,一次抓取可以服务较长时间。把两者在特征层做拼接,模型既能理解物品本身是什么,也能知道用户过去怎么对待类似物品。
举个例子,一个电商站点每天上新几千个SKU,如果只靠点击数据,这些SKU在前两周几乎无法被推到首页。但解析商品页HTML后,系统知道它们是夏季连衣裙且价位在200到300之间,就可以直接推给浏览过同类连衣裙的用户,显著提升转化率。
二、从HTML到特征的具体解析策略
要把HTML变成推荐系统的数据基础,第一步是可靠地解析文档。通常我们会用服务端爬虫或前端渲染后的接口拿到完整HTML字符串,然后借助解析库构建DOM树,再按规则提取节点。
下面以Python的BeautifulSoup为例,展示如何从一个简化的商品页提取关键字段。注意代码中的尖括号都已转义,仅作文本展示逻辑。
from bs4 import BeautifulSoup
html_text = '''
<html>
<body>
<h1 class="title">纯棉短袖T恤 男 夏季</h1>
<div class="breadcrumb">
<a href="/c1">服装</a> >
<a href="/c2">男装</a> >
<a href="/c3">T恤</a>
</div>
<span class="price">99.00</span>
<meta name="brand" content="simplelife" />
</body>
</html>
'''
soup = BeautifulSoup(html_text, 'html.parser')
title = soup.find('h1', class_='title').get_text(strip=True)
price = float(soup.find('span', class_='price').get_text(strip=True))
breadcrumb = [a.get_text(strip=True) for a in soup.select('div.breadcrumb a')]
brand = soup.find('meta', attrs={'name': 'brand'})['content']
print(title, price, breadcrumb, brand)
# 输出: 纯棉短袖T恤 男 夏季 99.0 ['服装', '男装', 'T恤'] simplelife
2.1 选择器规则的设计原则
实际项目中,网站结构会改版,因此选择器不能写死在业务代码深层。建议把抽取规则做成配置,例如用JSON描述哪个字段对应什么CSS路径或XPath。当页面微调时,只改配置而不动解析逻辑,降低维护成本。
另外要优先使用语义更明确的标签或属性,比如带有itemprop的微观数据、JSON-LD脚本块。很多电商站点在<script type="application/ld+json">中直接给出了商品结构化信息,解析它比猜class名稳妥得多。
2.2 处理动态渲染内容
现代站点常用前端框架异步加载数据,原始HTML可能只是空壳。这时需要通过无头浏览器拿到渲染后DOM,或者直接从接口拿到JSON再映射。虽然这增加了工程复杂度,但拿到的字段完整度更高,对推荐特征覆盖有好处。
如果资源有限,至少应保证标题、类目、价格这三个核心字段可解析,它们是内容推荐的底线特征。其余如评论数、评分可以后续通过接口补充,不一定非要从HTML硬取。
三、字段到推荐特征的映射与清洗
原始HTML文本并不能直接喂给模型,需要映射成算法友好的形式。文本类如标题要经过分词和去停用词,类目路径可转成多级one-hot或嵌入向量,价格需做分桶处理以避免量纲影响。
以下是一个简单的特征映射示例,把解析结果转成字典供后续向量化使用。
def build_feature(row):
# row来自解析结果: title, price, breadcrumb, brand
import re
text = re.sub(r's+', ' ', row['title'])
price_bucket = 'low' if row['price'] < 100 else ('mid' if row['price'] < 300 else 'high')
return {
'clean_title': text,
'category': row['breadcrumb'][-1],
'top_category': row['breadcrumb'][0],
'brand': row['brand'],
'price_level': price_bucket
}
sample = {'title': '纯棉短袖T恤 男 夏季', 'price': 99.0, 'breadcrumb': ['服装','男装','T恤'], 'brand': 'simplelife'}
print(build_feature(sample))
3.1 噪声与冲突处理
HTML里常有广告位、推荐位混在正文区,错误提取会把噪声引入特征。可以通过限定抽取区域,比如只取主商品容器内的节点,忽略侧边栏。同时对同一字段多来源做交叉校验,例如页面标题和JSON-LD里的名称不一致时,优先采用结构化数据。
对于文本中的特殊符号、HTML实体残留,要在清洗阶段统一处理。否则相似物品可能因符号差异被算成不相似,影响基于内容的召回效果。
3.2 特征版本管理
因为网站会改版,同一商品的HTML特征在不同时间可能不同。推荐系统应给特征打上抓取批次号,当算法效果下滑时,可回溯是哪批解析规则导致特征漂移。这也是把HTML数据纳入正规数据基础的一部分。
在工程上,建议把解析后的特征落表,与行为日志按商品ID关联,形成宽表。这样既方便离线训练,也能支持线上实时补全新物品画像。
四、落地架构与注意事项
将HTML数据用作推荐基础,典型链路是:抓取调度、解析服务、特征仓库、算法消费。解析服务最好无状态,方便横向扩容;特征仓库可用列式存储,便于按类目筛选。
需要特别提醒,解析外部站点要遵守对方爬虫协议与法律要求,避免抓取受限内容。对内网后台页面则应注意权限隔离,防止敏感字段外泄。
4.1 与现有推荐流程的衔接
如果已有协同过滤管线,可以在召回层并联一个内容召回:用HTML特征算物品间余弦相似度,取TopN补足冷启动物品。排序层则可把内容特征作为辅助维度,提升泛化能力。
从经验看,纯行为数据模型在老物品上更准,而HTML内容模型在新物品上更稳。两者融合往往比单用一种取得更好长期收益。
| 数据来源 | 更新频率 | 冷启动友好度 | 维护成本 |
|---|---|---|---|
| 用户行为日志 | 实时 | 低 | 中 |
| HTML解析特征 | 按抓取周期 | 高 | 较高 |
通过上述方式,HTML不再只是展示层 markup,而成为了推荐系统算法背后扎实的数据基石。合理设计解析与特征化流程,可以让推荐在内容理解上走得更远。