Angular 4 - 动态更新Facebook(开放图谱)的元标签

29

我们如何动态添加/更新元标签,以便Facebook / Whatsapp分享对话框能够获取它们?

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

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

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,因为我已经添加了具有默认值的静态标签。当我检查它们时,这段代码成功地更新了meta标签的值。

我知道Facebook/Whatsapp调试工具不执行任何javascript代码,所以它可能永远不会在它们的环境中执行。

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

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


1
我的问题是,有什么方法可以让Facebook/Whatsapp动态地获取更新的标签值?不,没有。Facebook只关心从服务器请求时URL返回的数据。因此,您需要某种形式的预渲染-可以在您自己的应用程序中实现,也可以通过一些外部服务(如prerender.io)实现。 - CBroe
我在下面添加了详细的答案。重要的一点是,Facebook缓存开放图谱信息。希望我们都能帮助您回答问题。请考虑选择正确答案旁边带有✔的答案。;-) - Manuel
7个回答

24

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

您需要提供一个包含open graph标签(如og:image,og:title和og:description)的静态html页面,并将其放置在html源代码中。因为Facebook、Twitter等只会抓取纯HTML而不会通过JavaScript渲染它。Angular通过JS动态更新DOM,因此爬虫只能获取初始的index.html。

有几种方法可以提供一个包含open graph标签的html文件:

  • 使用Angular Universal进行服务器端渲染
  • 使用代理渲染您的页面
  • 实时重写index.html并替换og标签
  • 提供静态html页面(不确定Angular是否支持)

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

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

我认为服务器端渲染是解决您问题的最合适的方式。为此,您可以托管一个节点服务器或使用例如AWS Lambda。这种方法的缺点是您的应用必须得到积极的托管,无法再以静态方式提供。无论如何,这似乎是最好的方式,因为它还可以提高SEO。Angular Universal是要搜索的术语:

在构建时对Angular Universal进行Prerendering

在构建过程中,您可以预渲染特定路由,并使用多个预渲染的index.html文件将angular作为静态应用程序提供。如果只有少量静态路由,则可以完美地解决这个问题。但是,如果考虑到具有动态部分的更通用路由,这不是一个解决方案。请考虑服务器端呈现。 angular通用模板 也包含了一个示例。请参见prerender.ts

替代方案

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

如果您想避免在构建过程中实现服务器端/预渲染(对于结构不良的应用程序来说,设置angular通用有时很麻烦),则可以尝试使用代理服务对页面进行预渲染。例如,请参阅prerender.io

覆盖index.html

将所有请求重定向到一个脚本,该脚本将覆盖og标记。例如,使用PHP和.htaccess覆盖og标记。现代环境也可以实现这一点。例如,您可以使用cloudfront / api gateway和lambda函数。尽管没有看到此类示例。

Facebook缓存和Open Graph调试

请注意,缓存可能仍然保留了其第一次抓取的Open Graph信息。确保您的源代码是最新的,并清除所有缓存、反向代理(如nginxx、cloudfront)。

使用Facebook调试器来调试Open Graph缓存并清除Facebook Open Graph缓存。


@Martijn Pieters 谢谢提醒!这是原始问题。我会更新我的答案在另一个问题中,并标记它们为重复的! - Manuel
好的回答。我想这可能回答了我的问题,但你认为我做错了什么?它不按照我想要的方式工作,因为FB只会使用默认的index.html值,对吗?我的问题链接:https://dev59.com/UGwNtIcB2Jgan1znALHf - Leon Horka
@Leon,你需要进行服务器端渲染。发送的HTML必须包含目标og参数,因为服务器不会执行JS,因为它已经回答了你的问题。 - Manuel

8
截至2018/19,如果您的主要目标是SEO(或者可能更多地是“SMO”——社交媒体优化——因为Googlebot在评估JavaScript方面做得很好,但大多数社交媒体机器人不行),那么您选择的SSR解决方案可能不应该是Angular Universal,而应该使用一个使用无头浏览器的解决方案。
这将属于Manuel答案中的“代理”类别,但由于我还没有看到它们在这里发布过,所以这里有两个(和半个)真正棒的解决方案:

Rendertron

这个解决方案由Google Chrome团队自己维护,是一个简单的端点,用于渲染您的应用程序并返回它。

Rendora

与Rendertron非常相似,但这个解决方案已经内置了中间件(即您决定哪些请求需要呈现,哪些不需要的位置和方式),并且还带有一些更高级但实用的功能,如缓存。因此,它非常接近于实现“零配置”,甚至比Rendertron更容易设置。

Puppeteer

再次由Google Chrome团队维护(实际上被Rendertron使用),Puppeteer为无头Chrome提供了基于节点的高级API。因此,如果之前的项目对您来说太过严格,您可能能够使用Puppeteer实现适当的解决方案,但显然这将需要比仅使用Rendertron或Rendora更多的工作。
与Angular Universal相比,这些解决方案具有巨大的优势,即您的应用程序项目可以完全对所使用的SSR工具保持不可知(甚至可以使用Angular以外的任何其他技术)。这显然不仅为您自己的代码提供了更大的灵活性,而且对于您的包选择也是如此,因为您不必担心它们是否与Angular Universal兼容。 它们的缺点可能是一些性能开销,但如果您只针对机器人,则可能并不重要。如果您使用Rendora的缓存,这甚至可能不成立,并且您实际上可能会获得性能提升。但是,如果与Angular Universal可以实现的性能提升相当,我就不知道了。但请记住,当我们谈论来自SSR的性能提升时,我们总是只谈论首次页面加载的时间。因此,通常这个重要性并不太高,因为您的用户在第一次加载后会与您的应用程序进行更多的交互。如果他们没有这样做,并且您主要有匿名用户仅检查一个页面然后离开,那么您可能首先不会构建PWA,而是传统的网页...

简而言之,请查看Rendora和Rendertron,它们可能是您正在寻找的,并且可以让您轻松快速地到达目的地。


4

使用 Facebook API 2.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);
});

1
您有此内容的来源吗?即使此代码位于Angular组件中,Facebook加载页面时也会调用此脚本吗? - CodyBugstein
你需要在你的控制器中发起请求,例如:share() { FB.ui(...) }。 - rlarin
谢谢!非常清晰和有用。正是我所需要的。 - Giuseppe

2
如果您正在使用Angular 4,为什么不使用Angular Universal创建服务器端页面 - 这样您就可以在浏览器加载页面之前以编程方式构建HEAD标签。请参考https://universal.angular.io/

3
这是一个庞大的应用程序,我认为在目前测试阶段无法完全改变方法。它已经建立在ASP.NET MVC框架之上。 - Ali Baig
如果不是通用的话,那么您必须在服务器上进行这些 API 调用以获取数据,并将元标记与 ASP.NET 渲染的文档一起发送。不能保证 fb 或 whatsapp 将启用 js 来提取 js 修改的标记并爬取您的页面。 - pxwise

1

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

  1. 从您的API中获取所需数据。
  2. 返回带有og标签的简单HTML网站
  3. 使用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>

0

那个htaccess文件是做什么用的? - DFSFOT

0

只是在khushali的答案中加入了2分钱,这帮助我找到了一个临时解决方案。

在我的托管提供商(Dreamhost)上,当我只是复制/粘贴时,[NC,OR]会产生奇怪的结果。对于只有一行的RewriteCond,我必须将其编写为 RewriteCond … googlebot|yandex|…|…|… [NC] (将RewriteCond重新编写为每行一个也可以工作,但不适用于第一行的[OR]。这将起作用:)

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

请注意第一行似乎缺少

另一方面,我的第二个建议是关于最后一个WhatsApp条目- 结果WhatsApp直接从应用程序内部进行了爬取(至少今天我的安卓手机是这样的;) 所以我的完整代码现在是 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://dev59.com/JWMl5IYBdhLWcg3wbGl1
        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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接