单页应用程序 (SPA) 由客户端呈现模板提供支持,为最终用户提供非常动态的体验。最近,Google 宣布他们会像普通用户一样抓取网页并执行 JavaScript,从而使由 SPA 框架(Angular、Ember 和 Vue 等)支持的网站在不受 Google 惩罚的情况下进行抓取。
除了搜索之外,其他网络爬虫对于您网站的可见性也很重要,即依赖元标记的丰富社交共享机器人仍然对 JavaScript 视而不见。
在本教程中,我们将为您的 Express 和 Node.js 服务器构建一个备用路由和渲染模块,您可以将其与大多数 SPA 框架一起使用,并使您的网站能够在 Twitter、Facebook 和 Pinterest 上进行丰富的共享。
警告语
本教程专门讨论提取社交共享信息的网络机器人。 不要在搜索引擎网络爬虫中尝试这种技术。搜索引擎公司可能会认真对待这种行为,并将其视为垃圾邮件或欺诈行为,因此您的排名可能会很快下降。
同样,通过丰富的社交共享信息,请确保您所呈现的内容与用户所看到的内容与机器人所读到的内容保持一致。未能保持这种一致性可能会导致社交媒体网站的限制。
丰富的社交分享
如果您在 Facebook 中发布更新并包含 URL,Facebook 机器人将读取 HTML 并查找 OpenGraph 元标记。以下是 Envato Tuts+ 主页的示例:
检查页面,在 head
标记中,以下是生成此预览的相关标记:
Pinterest 使用与 Facebook、OpenGraph 相同的协议,因此它们的共享工作原理大致相同。
在 Twitter 上,这个概念被称为“卡片”,并且 Twitter 有几种不同的变体,具体取决于您想要如何呈现内容。以下是来自 GitHub 的 Twitter 卡示例:
这是生成此卡片的 HTML:
注意:GitHub 使用与本教程中描述的类似技术。页面 HTML 与名称属性设置为 twitter:description
的标签略有不同。我必须按照本文后面所述更改用户代理才能获得正确的元标记。
客户端渲染的问题
如果您只需要整个网站的一个标题、描述或图像,添加元标记不是问题。只需将这些值硬编码到 HTML 文档的 head
中即可。然而,您可能正在构建一个更加复杂的网站,并且您希望丰富的社交共享根据 URL 的不同而变化(这可能是您的框架正在操作的 HTML5 History API 的包装器)。
第一次尝试可能是构建模板并将值添加到元标记,就像添加任何其他内容一样。由于此时提取此信息的机器人不会执行 JavaScript,因此在尝试共享时,您最终会得到模板标签,而不是预期的值。
为了使网站能够被机器人读取,我们正在构建一个中间件来检测社交共享机器人的用户代理,然后构建一个备用路由器,为机器人提供正确的内容,从而避免使用 SPA 框架。
用户代理中间件
客户端(机器人、网络爬虫、浏览器)在每个请求的 HTTP 标头中发送用户代理 (UA) 字符串。这应该可以识别客户端软件;虽然网络浏览器有各种各样的 UA 字符串,但机器人往往或多或少都稳定。 Facebook、Twitter 和 Pinterest 出于礼貌发布其机器人的用户代理字符串。
在 Express 中,UA 字符串作为 user-agent
包含在 req
uest 对象中。我使用正则表达式来识别我有兴趣提供替代内容的不同机器人。我们将把它包含在中间件中。中间件就像路由,但它们不需要路径或方法,并且它们(通常)将请求传递给另一个中间件或路由。在 Express 中,路由和中间件是连续的,因此请将其放置在 Express 应用中的任何其他路由之上。
app.use(function(req,res,next) { var ua = req.headers['user-agent']; if (/^(facebookexternalhit)|(Twitterbot)|(Pinterest)/gi.test(ua)) { console.log(ua,' is a bot'); } next(); });
上面的正则表达式在 UA 字符串的开头查找“facebookexternalhit”、“Twitterbot”或“Pinterest”。如果存在,则会将 UA 记录到控制台。
这是整个服务器:
var express = require('express'), app = express(), server; app.use(function(req,res,next) { var ua = req.headers['user-agent']; if (/^(facebookexternalhit)|(Twitterbot)|(Pinterest)/gi.test(ua)) { console.log(ua,' is a bot'); } next(); }); app.get('/',function(req,res) { res.send('Serve SPA'); }); server = app.listen( 8000, function() { console.log('Server started.'); } );
测试您的中间件
在 Chrome 中,导航到您的新服务器(应该是 http://localhost:8000/
)。打开 DevTools 并通过单击开发人员窗格左上方的智能手机图标打开“设备模式”。
在设备工具栏上,将“Twitterbot/1.0”放入UA编辑框中。
现在,重新加载页面。
此时,您应该在页面中看到“Serve SPA”,但是查看 Express 应用程序的控制台输出,您应该看到:
Twitterbot/1.0 是一个 bot
备用路由
现在我们可以识别机器人了,让我们构建一个备用路由器。 Express 可以使用多个路由器,通常用于按路径划分路由。在这种情况下,我们将以稍微不同的方式使用路由器。路由器本质上是中间件,因此除了 req
、res
和 next
之外,就像任何其他中间件一样。这里的想法是生成一组具有相同路径的不同路由。
nonSPArouter = express.Router(); nonSPArouter.get('/', function(req,res) { res.send('Serve regular HTML with metatags'); });
我们的中间件也需要更改。现在,我们不再只是记录客户端是机器人,而是将请求发送到新路由器,重要的是,如果 UA 测试失败,则仅将其与 next()
一起传递。因此,简而言之,机器人获得一台路由器,其他人获得为 SPA 代码提供服务的标准路由器。
var express = require('express'), app = express(), nonSPArouter = express.Router(), server; nonSPArouter.get('/', function(req,res) { res.send('Serve regular HTML with metatags'); }); app.use(function(req,res,next) { var ua = req.headers['user-agent']; if (/^(facebookexternalhit)|(Twitterbot)|(Pinterest)/gi.test(ua)) { console.log(ua,' is a bot'); nonSPArouter(req,res,next); } else { next(); } }); app.get('/',function(req,res) { res.send('Serve SPA'); }); server = app.listen( 8000, function() { console.log('Server started.'); } );
如果我们使用与上面相同的例程进行测试,将 UA 设置为 Twitterbot/1.0
浏览器将在重新加载时显示:
使用元标记
提供常规 HTML
使用标准 Chrome UA,您将获得:
服务SPA
元标签
正如我们上面所讨论的,丰富的社交共享依赖于 HTML 文档头部内的 meta
标签。由于您正在构建 SPA,因此您甚至可能没有安装模板引擎。在本教程中,我们将使用玉。 Jade 是一种相当简单、诱人的语言,其中空格和制表符是相关的,并且不需要结束标签。我们可以通过运行来安装它:
npm 安装jade
在我们的服务器源代码中,在 app.listen
之前添加此行。
app.set('视图引擎', '玉石');
现在,我们将仅输入想要提供给机器人的信息。我们将修改 nonSPArouter
。由于我们已经在应用程序集中设置了视图引擎,因此 res.render
将进行玉石渲染。
让我们设置一个小玉模板来服务于社交共享机器人:
doctype html html head title= title meta(property="og:url" name="twitter:url" content= url) meta(property="og:title" name="twitter:title" content= title) meta(property="og:description" name="twitter:description" content= descriptionText) meta(property="og:image" content= imageUrl) meta(property="og:type" content="article") meta(name="twitter:card" content="summary") body h1= title img(src= img alt= title) p= descriptionText
这个模板的大部分内容是 meta
标签,但您也可以看到我在文档正文中包含了这些信息。在撰写本教程时,似乎没有一个社交共享机器人实际上会查看元标记之外的任何其他内容,但如果在以下位置实施任何类型的人工检查,则最好以某种人类可读的方式包含信息:稍后的日期。
将模板保存到应用程序的 view
目录并将其命名为 bot.jade
。不带扩展名的文件名(“bot”)将是 res.render 函数的第一个参数。
虽然在本地开发总是一个好主意,但您将需要在其最终位置公开您的应用程序以完全调试您的 meta
标记。我们的小型服务器的可部署版本如下所示:
var express = require('express'), app = express(), nonSPArouter = express.Router(), server; nonSPArouter.get('/', function(req,res) { var img = 'placeholder.png'; res.render('bot', { img : img, url : 'https://bot-social-share.herokuapp.com/', title : 'Bot Test', descriptionText : 'This is designed to appeal to bots', imageUrl : 'https://bot-social-share.herokuapp.com/'+img }); }); app.use(function(req,res,next) { var ua = req.headers['user-agent']; if (/^(facebookexternalhit)|(Twitterbot)|(Pinterest)/gi.test(ua)) { console.log(ua,' is a bot'); nonSPArouter(req,res,next); } else { next(); } }); app.get('/',function(req,res) { res.send('Serve SPA'); }); app.use('/',express.static(__dirname + '/static')); app.set('view engine', 'jade'); server = app.listen( process.env.PORT || 8000, function() { console.log('Server started.'); } );
另请注意,我使用 express.static
中间件来提供 /static
目录中的图像。
调试您的应用
将应用程序部署到可公开访问的位置后,您应该验证您的 meta
标记是否按预期工作。
首先,您可以使用 Facebook 调试器进行测试。输入您的网址并点击获取新的抓取信息。
您应该看到类似以下内容:
接下来,您可以继续使用 Twitter 卡验证器测试您的 Twitter 卡。对于此操作,您需要使用 Twitter 帐户登录。
Pinterest 提供了一个调试器,但此示例无法开箱即用,因为 Pinterest 只允许在主页以外的 URL 上使用“丰富的 pin”。
后续步骤
在实际实施中,您需要处理数据源和路由的集成。最好查看 SPA 代码中指定的路由,并为您认为可能共享的所有内容创建替代版本。建立可能共享的路由后,在主模板中设置 meta
标记,当有人共享您不希望的页面时,该标记可作为您的后备。
虽然 Pinterest、Facebook 和 Twitter 占据了社交媒体市场的很大一部分,但您可能还想集成其他服务。有些服务确实会公布其社交共享机器人的名称,而其他服务则可能不会。要确定用户代理,您可以 console.log
并检查控制台输出 - 首先在非生产服务器上尝试此操作,因为在繁忙的站点上尝试确定用户代理可能会很困难。从那时起,您可以修改我们的中间件中的正则表达式来捕获新的用户代理。
丰富的社交媒体分享是将人们吸引到您精美的基于单页应用程序的网站的好方法。通过有选择地将机器人引导至机器可读的内容,您可以向机器人提供正确的信息。