Skip to content抓取 HTML 并提取
搜索引擎如何理解页面:从文字到实体
在早期的信息检索中,搜索引擎将网页看作一个词项集合——把标题、正文、链接锚文本拆成词项,放入倒排索引。查询时按词匹配算分,即可排序。
但当用户搜索“明天会不会下雨”时,仅靠词项匹配不可能给出直接答案。搜索引擎需要把“明天”识别为一个日期实体、“下雨”识别为一种天气状况。这其中依赖实体识别与知识图谱。
网页内容也是如此。一段文字提到《三体》、刘慈欣、重庆出版社,搜索引擎如果能将这些字符串识别为 Book、Person、Organization 实体,就能理解页面主题与关联,而不是只统计词频。这个识别过程一部分靠自然语言处理,另一部分则依赖页面作者主动提供的结构化数据。
结构化数据本质上是用一套机器可精确解析的词汇和格式,声明“这个页面描述的是一个人”“这个页面关于一部电影”“这个产品有价格和库存状态”。它不改变页面外观,只增加一层机器可读的标记。
Schema.org:一套通用的语义词汇
添加标记需要共享词汇表。Schema.org 是由 Google、Microsoft、Yahoo! 等共同维护的项目,定义了一套类型(Type)和属性(Property),覆盖 CreativeWork、Event、Organization、Person、Place、Product 等概念[6]。
HTML 的 <h1> 告诉浏览器“这是最重要的标题”,而 Schema.org 的类型与属性则告诉搜索引擎“这是一个食谱,拥有名称、烹饪时间、食材和步骤”。Google 搜索中,大部分结构化数据都使用 Schema.org 词汇,但 Google 仅支持其中的一个子集[5]。因此实际使用时应查阅 Google 搜索中心文档,确认哪些类型与属性能触发富媒体搜索结果。
目前推荐的注入方法是在 HTML 中嵌入 JSON-LD。
JSON-LD 基本语法
JSON-LD 是一种使用 JSON 对象表示 linked data 的格式。它不侵入 HTML 标签,而是独立放在 <script> 块内,Google 可以安全解析。
一个最简单的 JSON-LD 块如下:
html
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "刘慈欣",
"url": "https://example.com/liu-cixin"
}
</script>@context声明词汇表,这里固定为"https://schema.org"。@type指明当前实体类型,必须是 Schema.org 定义的类型,如Person、Article、Product。- 其他属性按类型语法填写。以上标记声明了一个人,名为“刘慈欣”,并给出其主页链接。
整个 JSON 对象必须是合法的 JSON——不能有尾逗号,属性名需加双引号。此外可通过嵌套对象表示关系,例如:
html
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "气候变化的十个事实",
"author": {
"@type": "Person",
"name": "李四"
}
}
</script>这里 author 的属性值是一个 Person 类型实体,描述文章作者。
在网页中嵌入 JSON-LD 的常见模式
JSON-LD 可以放在 <head> 或 <body> 内,Google 都会处理。常见做法有:
- 网站级全局标记(如
Organization或WebSite)放入所有页面共享的模板头部。 - 页面内容标记(如
Article、Product、BreadcrumbList)放在对应页面的<head>或<body>中,紧邻页面主要内容。
标记应描述当前页面的主体信息,不应添加与用户看不见的内容相关的数据,也不应创建空白页面仅用于容纳结构化数据[11]。
常用 Schema.org 类型与关键属性
下面列出几个常见于 SEO 场景的类型及其关键属性。
WebSite
描述网站本身,常用于首页,可触发站点名称在搜索结果中的展示。
json
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "一家书店",
"url": "https://www.example.com"
}Article
适用于文章页面(博客、新闻等)。常用属性[9]:
headline:文章标题。author:作者,类型为Person或Organization。若有多个作者,每位作者分别写一个author条目,不要合并到一个字段里[10]。datePublished:首次发布日期,使用 ISO 8601 格式(如"2025-03-15T10:00:00+08:00")。dateModified:最后修改日期,同上。
BreadcrumbList
用于面包屑导航,帮助搜索引擎理解页面层级。
json
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "首页",
"item": "https://www.example.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "博客",
"item": "https://www.example.com/blog/"
},
{
"@type": "ListItem",
"position": 3,
"name": "气候变化的十个事实"
}
]
}FAQ
用于问答板块,可能在搜索结果中直接展示问与答。
json
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "如何退货?",
"acceptedAnswer": {
"@type": "Answer",
"text": "在收货后30天内联系我们。"
}
}
]
}Product 与 Offer
描述产品及其报价,常用于电子商务页面。
json
{
"@context": "https://schema.org",
"@type": "Product",
"name": "牛皮纸笔记本",
"offers": {
"@type": "Offer",
"price": "29.00",
"priceCurrency": "CNY",
"availability": "https://schema.org/InStock"
}
}实际使用时,先确定页面主题,再到 Schema.org 及 Google 搜索中心文档中找到最匹配的类型,然后按文档填写推荐属性。
示例:为页面添加 WebSite 与 Article 结构化数据
为一个博客网站添加两种标记:首页的 WebSite,文章页的 Article。
假设首页为 https://www.example.com/,文章页为 https://www.example.com/blog/climate-facts.html。
在首页 <head> 中加入:
html
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "一家书店",
"url": "https://www.example.com"
}
</script>在文章页中加入:
html
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "气候变化的十个事实",
"author": {
"@type": "Person",
"name": "李四"
},
"datePublished": "2025-02-10T09:00:00+08:00",
"dateModified": "2025-03-01T14:00:00+08:00",
"publisher": {
"@type": "Organization",
"name": "一家书店"
}
}
</script>这里的 Article 标记还嵌套了 publisher 组织实体,以便 Google 聚合发布者信息。注意 author 中不要添加职称或 publisher 字段[10]。
验证与调试:让测试工具替你检查
写完标记后,先使用 Google 的富媒体搜索结果测试(Rich Results Test)进行验证。该工具可以检测结构化数据是否能触发 Google 搜索的富媒体功能,例如文章能否带标题、作者、发布日期等增强展示。它会告诉你哪些属性缺失、哪些值格式不对。
打开 https://search.google.com/test/rich-results,输入页面 URL 或直接粘贴 HTML 代码,即可看到解析结果和错误提示。开发阶段通常直接粘贴代码片段,比线上 URL 测试更快。
部署后,还可以通过 Google Search Console 的“富媒体搜索结果状态”报告持续监控已部署页面的有效性,但这一环节不在本节展开。
Node.js 实战:提取并解析页面中的 JSON-LD
抓取 HTML 并提取 script[type="application/ld+json"]
使用 Node.js 内置 fetch(Node.js 18 及以上已内置)获取页面,再通过 cheerio 解析 HTML,抓取所有 JSON-LD 脚本块。
安装依赖:
bash
npm install cheerio示例代码 extract-jsonld.js:
javascript
import cheerio from 'cheerio';
async function fetchAndExtractJSONLD(url) {
const res = await fetch(url);
const html = await res.text();
const $ = cheerio.load(html);
const scripts = $('script[type="application/ld+json"]');
const jsonldList = [];
scripts.each((i, el) => {
try {
const json = JSON.parse($(el).html());
jsonldList.push(json);
} catch (e) {
console.error(`解析第 ${i} 个 JSON-LD 时出错:`, e.message);
}
});
return jsonldList;
}
// 用法
const result = await fetchAndExtractJSONLD(
'https://www.example.com/blog/climate-facts.html'
);
console.log(result);如果页面包含前面的 Article 标记,控制台输出类似:
[
{
'@context': 'https://schema.org',
'@type': 'Article',
headline: '气候变化的十个事实',
author: { '@type': 'Person', name: '李四' },
datePublished: '2025-02-10T09:00:00+08:00',
dateModified: '2025-03-01T14:00:00+08:00',
publisher: { '@type': 'Organization', name: '一家书店' }
}
]若使用 Node.js 低于 18 的版本,无法直接使用全局 fetch,可以选择 node-fetch 或 axios。例如使用 node-fetch:
javascript
import fetch from 'node-fetch';
import cheerio from 'cheerio';
// 其余代码保持一致解析 JSON 并校验 @context 与 @type
获取 JSON 对象列表后,可进行最基本的结构检查:
javascript
function validateJSONLD(jsonld) {
const errors = [];
if (!jsonld['@context'] || jsonld['@context'] !== 'https://schema.org') {
errors.push('缺少或错误的 @context');
}
if (!jsonld['@type']) {
errors.push('缺少 @type');
}
return errors.length === 0 ? null : errors;
}
// 逐一检查
result.forEach((item, idx) => {
const errs = validateJSONLD(item);
if (errs) {
console.log(`第 ${idx + 1} 个 JSON-LD 有误:`, errs);
} else {
console.log(`第 ${idx + 1} 个是合法的 ${item['@type']} 标记`);
}
});这段校验会判断每个 JSON-LD 块是否包含正确的上下文和类型。实际项目中可扩展检查必填属性及数据类型格式。
结构化数据常见错误排查
在实施过程中,常见错误大致有以下几类:
JSON 语法错误
尾逗号、不配对的大括号或引号会导致解析失败。可先用任意 JSON 校验器预检。富媒体结果测试也会直接提示“JSON-LD 解析错误”。@context 写错
例如漏写或写成"http://schema.org"(缺少 https),搜索引擎将无法正确解读。类型与属性不匹配
对Person使用price等不相干属性,或对Article的author填入一个字符串而非 Person/Organization 对象,标记可能无效。必填属性缺失
Google 针对每种类型定义了推荐和必要属性。例如 Article 标记若要影响搜索结果的增强展示,headline、author、datePublished通常必须存在。缺失不一定会报错,但不会触发富媒体功能。嵌套错误
有些属性要求值为对象,却写了字符串。例如publisher应为Organization对象,不能是纯字符串。
检查时优先将 JSON-LD 代码复制到富媒体结果测试中查看结果。如果是通过 Node.js 提取的数据,可以先本地校验,再人工复现异常情形。
