Angular 4 - 为 Facebook 动态更新 Meta 标签(打开图表)

Posted

技术标签:

【中文标题】Angular 4 - 为 Facebook 动态更新 Meta 标签(打开图表)【英文标题】:Angular 4 - Update Meta tags dynamically for Facebook (Open graph) 【发布时间】:2017-12-28 23:37:55 【问题描述】:

我们如何动态添加/更新元标签,以便它们被 Facebook/Whatsapp 共享对话框选中?

我将我的 Angular 2 应用程序升级到 Angular 4,以便在我们从 API 获取组件中的数据后使用 Meta 服务动态添加/更新元标记。

到目前为止,在我的组件中,我有

this.metaService.updateTag( property: 'og:title', content: pageTitle );
this.metaService.updateTag( property: 'og:url', 'www.domain.com/page' );
this.metaService.updateTag( property: 'og:image', content: coverUrl, itemprop: 'image' );
this.metaService.updateTag( property: 'og:image:url', content: coverUrl, itemprop: 'image' );
this.metaService.updateTag( property: 'og:image:type', content: 'image/png' );

我正在使用 updateTag,因为我已经添加了具有默认值的静态标签。当我检查元标记值时,此代码成功更新了它们。

我知道 Facebook/Whatsapp 调试器工具不执行任何 javascript 是有道理的,因此它可能永远不会在他们的环境中执行。

我正在使用https://developers.facebook.com/tools/debug/,它总是选择有意义的默认标签值。

我的问题是,如何让 Facebook/Whatsapp 动态获取更新的标签值?我正在使用 Angular 4 并通过 API 调用加载所有数据,因此在页面加载和脚本执行之前无法获取任何类型的数据。

【问题讨论】:

“我的问题是,有什么方法可以让 Facebook/Whatsapp 动态获取更新的标签值?” - 不,没有。 Facebook 只关心从服务器请求时 URL 返回的数据。所以你需要某种预渲染——要么在你自己的应用中实现,要么通过一些外部服务,比如 prerender.io 我在下面添加了一个广泛的答案。只是重要的节点,facebook 缓存打开的图信息。希望我们都帮助您回答您的问题。考虑接受首选答案旁边带有 ✔ 的正确答案 ;-) 【参考方案1】:

Open Graph OG 标签必须在源代码中!

您需要在 html 源代码中提供包含开放图形标签(如 og:image og:title 和 og:description)的静态 html 页面,因为 facebook、twitter 和 co 只是抓取纯 html 而不通过 javascript 呈现它. Angular 通过 js 动态更新 dom,因此爬虫只获取初始 index.html。

有几种方法可以提供包含开放图的 html 标签 ans 解决您的问题:

使用 Angular Universal 进行服务器端渲染 使用代理呈现您的页面 即时覆盖 index.html 替换 og 标记 提供静态 html 页面(不确定 Angular 是否支持)

我猜你已经使用 ngx-meta 之类的东西来添加 og 标签?

Angular Universal - 在 Angular 2/3/4/5 中使用元标记进行服务器端渲染

我猜服务器端渲染是解决您的问题的最合适的方法。为此,您可以托管节点服务器或使用例如。 AWS 拉姆达。这样做的缺点是,您的应用程序必须主动托管并且不能再静态提供服务。无论如何,这似乎是最好的方法,因为它也可以改善 SEO。 Angular Universal 是要搜索的术语:

Angular Universal Angular Universal Starter Boilerplate Angular Universal PWA with serverside rendering serverless with aws lambda boilerplate

Angular Universal Prerendering on build

您还可以在构建过程中预渲染特定的路由,并将 Angular 用作具有多个预渲染 index.html 文件的静态应用程序。如果您只有很少的静态路由,则此方法非常好。考虑具有动态部分的更通用的路线,这不是解决方案。去服务器端渲染。 angular universal boilerplate 也包含一个例子。见prerender.ts

替代解决方案

使用代理预渲染 Angular 以提供 OG 标签

如果您想避免在构建过程中实现服务器端/预渲染(设置 Angular Universal 有时对于结构不佳的应用来说是一件痛苦的事情。)您可以尝试使用代理服务来预渲染您的页面。看看例如。 prerender.io。

覆盖 index.html

将所有请求重定向到覆盖 og:tags 的脚本。例如。 Using php and .htaccess to overwrite og tags 这在现代环境中也是可能的。例如。您可以使用 cloudfront / api 网关和 lambda 函数。不过还没有看到这样的例子。

Facebook Cache 和 Open Graph 调试

请注意,缓存可能仍会缓存第一次抓取时打开的图形信息。确保您的源代码是最新的,并且所有缓存、反向代理(如 nginxx、cloudfront)都已清除。

使用Facebook Debugger调试开放图缓存并清除facebook开放图缓存

【讨论】:

@Martijn Pieters 感谢您的注意!这是原始问题。更新我在另一个问题中的答案并将其标记为重复! 好答案。我想这可能会回答我的问题,但是您认为我做错了什么?它不能按我想要的方式工作,因为 FB 只适用于默认的 index.html 值,对吗?我的问题:***.com/questions/70174838/… @Leon,你需要做服务器端渲染。您发送的 html 必须包含目标 og 参数,因为服务器不会执行 JS,因为它已经回答了您的问题。【参考方案2】:

截至 2018/19 年,如果您的主要目标是 SEO(或者可能更多“SMO” - 社交媒体优化 - 因为 Googlebot 在评估 JavaScript 方面做得很好,但大多数社交媒体机器人没有)您的 s-s-r 解决方案选择可能不应该是 Angular Universal,而是使用无头浏览器的东西。

这属于 Manuel 回答的“代理”类别,但由于我还没有看到他们在这里发布两个(半)非常好的解决方案:

Rendertron

这是由 Google Chrome 团队自己维护的,它只是一个用于渲染和返回应用的绝佳端点。

Rendora

与 Rendertron 非常相似,但它具有已内置的中间件(即您在何处以及如何决定哪些请求被呈现,哪些不被呈现),并且还带有一些更高级但更方便的功能,例如缓存。因此,它确实非常接近它的“零配置”目标,甚至比 Rendertron 更容易设置。

Puppeteer

再次由 Google Chrome 团队维护(Rendertron 实际使用)Puppeteer 为无头 Chrome 提供了一个基于节点的高级 API。因此,如果以前的项目对您来说是两个僵硬的项目,您可能可以使用 Puppeteer 实现一个合适的解决方案,但显然它比仅使用 Rendertron 或 Rendora 需要更多的工作。

与 Angular Universal 相比,此解决方案具有巨大优势,即您的应用项目可以完全不依赖于使用的 s-s-r 工具(它甚至可以使用除 Angular 之外的任何其他技术)。这显然不仅为您自己的代码提供了更大的灵活性,而且还为您的包选择提供了更多的灵活性,因为您不必担心它们是否与 Angular Universal 兼容。 它们的缺点可能是一点性能开销,但如果你只是针对机器人,这可能无关紧要。如果您使用 Rendora 的缓存,这甚至可能不是真的,您实际上可能会提高性能。但是,如果这可以与您使用 Angular Universal 实现的性能提升相媲美,我不知道。但请记住,当我们谈论 s-s-r 的性能提升时,我们总是只谈论加载首页的时间。所以通常这一点的重要性并不太高,因为您的用户在首次加载后会与您的应用进行更多交互。如果他们不这样做,并且您主要是匿名用户,他们只是检查一个页面然后离开,那么您可能首先不会构建 PWA,而是构建经典网页......

tl;dr 只需查看 Rendora 和 Rendertron,它们可能就是您正在寻找的东西,可以让您轻松快速地到达那里。

【讨论】:

【参考方案3】:

试试这个(使用 fb API:v2.12):

FB.ui(
  method: 'share_open_graph',
  action_type: 'og.shares',
  action_properties: JSON.stringify(
    object : 
      'og:url': 'url', // your url to share
      'og:title': 'title',
      'og:site_name':'site_name',
      'og:description':'description',
      'og:image': 'image Url',//
      'og:image:width':'250',//size of image in pixel
      'og:image:height':'257'
    
  )
, function(response)
  console.log("response is ",response);
);

【讨论】:

你有这方面的资料吗? Facebook 会在加载您的页面时调用此脚本,即使此代码在 Angular 组件中? 您需要在控制器中发出请求,例如:share() FB.ui(...) 谢谢!如此清晰和有用。正是我需要的。【参考方案4】:

如果您使用 Angular 4,为什么不使用 Angular Universal 创建页面服务器端 - 这样您就可以在浏览器加载页面之前以编程方式构建您的 HEAD 标签

https://universal.angular.io/

【讨论】:

它是一个大型应用程序,并且认为在这个阶段(在测试中)完全改变方法是不可能的。它已经建立在 ASP.NET MVC 之上 如果不是通用的,那么您必须在服务器上进行这些 api 调用以获取该数据并将元标记与 ASP.NET 呈现的文档一起发送。不保证 fb 或 whatsapp 会在启用 js 的情况下抓取您的页面以获取 js 修改后的标签。【参考方案5】:

在角度 6 中, 动态元标记不反映在 index.html 中

因此,只有借助 .htaccess 才能获取动态元内容。

如果您想呈现需要的动态内容,请借助 .htaccess。

RewriteCond %HTTP_USER_AGENT facebookexternalhit/1.1|Twitterbot|Pinterest|linkedinbot|WhatsApp|Viber|SkypeUriPreview|Google.*sn-p [NC,OR]

更多信息:

https://gist.github.com/thoop/8072354

https://www.winhelp.info/create-browser-whitelist-with-htaccess.html

【讨论】:

htaccess 是做什么的?【参考方案6】:

只需在 khushali 的回答中增加 2 美分,这对我的临时解决方案有所帮助。

在我的托管服务提供商 (Dreamhost) 上,[NC,OR] 仅在复制/粘贴时产生了奇怪的结果。在只有一行的 RewriteCond 上,我不得不把它写成 RewriteCond … googlebot|yandex|…|…|… [NC] (用每行一个重写 RewriteCond 也有效,但在 first 行上使用 [OR] 无效。这可行:)

RewriteCond … googlebot [NC]
RewriteCond … yandex [NC,OR]
RewriteCond … WhatsApp [NC,OR]

注意第一行中看似缺失的OR

另一方面,我的第二分钱是最后一个 WhatsApp 条目 - 结果表明 WhatsApp 直接从应用程序内部进行抓取(至少它今天在我的 android 手机上进行了;)所以我的完整线路现在是 RewriteCond %HTTP_USER_AGENT googlebot|bingbot|yandex|baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora\ link\ preview|showyoubot|outbrain|pinterest\/0\.|pinterestbot|slackbot|vkShare|W3C_Validator|WhatsApp [NC]

(还有我的完整 htaccess

    RewriteEngine On
        
        # https://***.com/questions/18406156/redirect-all-to-index-php-using-htaccess
        RewriteCond %REQUEST_FILENAME !-f
        RewriteCond %REQUEST_FILENAME !-d
        RewriteCond %HTTP_USER_AGENT googlebot|bingbot|yandex|baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora\ link\ preview|showyoubot|outbrain|pinterest\/0\.|pinterestbot|slackbot|vkShare|W3C_Validator|WhatsApp [NC]
        #        RewriteCond %HTTP_USER_AGENT facebookexternalhit|googlebot [NC]   MUST BE WRITTEN WITHOUT OR
        #        RewriteCond %HTTP_USER_AGENT googlebot [NC]
        #        RewriteCond %HTTP_USER_AGENT facebookexternalhit [NC,OR]          'OR' IS FOR SECOND LINE (AND THIRD AND FOURTH ETC. WON'T WORK ON FIRST LINE)
        RewriteRule ^(.*)$ opengraph.php?q=$1 [NC,L,QSA]

        RewriteCond %REQUEST_FILENAME !-f
        RewriteCond %REQUEST_FILENAME !-d
        RewriteRule ^(.*)$ redir.php?orig_path=$1 [NC,L,QSA]

</IfModule>

【讨论】:

【参考方案7】:

我刚刚为链接创建了一个简单的 PHP 站点,它实现了 Open Graph 标记并通过 JavaScript 将用户重定向到“真实”站点。该脚本执行以下操作:

    从您的 API 中获取所需的数据。 返回带有 og 标记的简单 HTML 站点 使用 JavaScript 将用户重定向到“真实”站点

例子:

<?php
  $articleId = $_GET['id'];
  $redirectUrl = 'https://yourapp.com/app/tabs/start/article/'.$articleId;

  // get the article metadata
  $response = file_get_contents('https://api.yourapp.com/articles/'.$articleId);
  $response = json_decode($response);

  $title = $response->title;
  $description = $response->excerpt;
  if(property_exists ($response, 'mainImageUrl') ) 
    $imageUrl = $response->mainImageUrl;
  
  $publishedTime = $response->published;
?>

<html prefix="og: http://ogp.me/ns#">
<head>
  <title><?php echo $title ?></title>
  <meta name="description" content="<?php echo $description ?>">
  <meta property="og:title" content="<?php echo $title ?>">
  <meta property="og:description" content="<?php echo $description ?>">
  <meta property="og:site_name" content="Your App">
  <meta property="og:locale" content="en_US">
  <meta property="og:type" content="article">
  <meta property="og:url" content="https://yourapp.com/article/<?php echo $articleId ?>">
  <?php if(isset($imageUrl))  echo '<meta property="og:image" content="'.$imageUrl.'">';  ?>
  <meta property="og:image" content="<?php echo $imageUrl ?>">
  <meta property="article:published_time" content="<?php echo $publishedTime ?>">

  <script>
    window.location.href = '<?php echo $redirectUrl ?>';
  </script>
</head>
<body>
    <a href="<?php echo $redirectUrl ?>">Click here to proceed...</a>
</body>
</html>

【讨论】:

以上是关于Angular 4 - 为 Facebook 动态更新 Meta 标签(打开图表)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 中创建像 Facebook 帖子这样的照片网格?

具有来自 Web 服务的动态元数据的 Angular 6 元服务

Angular 4 CLI,WebPack,为整个网站动态切换引导主题

在 Angular 4/5 中,是不是可以只预呈现来自 Facebook 和 Twitter 爬虫的请求,而不是预呈现每个请求

将Angular App发布为facebook Instant Article

Angular 4:带插值的动态模板