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

时间:2017-07-23 07:44:33

标签: angular facebook-graph-api facebook-opengraph meta-tags

我们如何动态添加/更新元标记,以便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调用加载所有数据,因此在页面加载和执行脚本之前无法获取任何类型的数据。

7 个答案:

答案 0 :(得分:12)

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

你需要提供一个静态的html页面,其中包含og:image og:title和og:html源代码中的开放图形标签,因为facebook,twitter和co只是抓取普通html而不通过javascript渲染它。 Angular通过js动态更新dom,因此抓取工具只获得初始index.html。

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

  • 使用角度通用的Serverside渲染
  • 使用代理呈现您的网页
  • 直接覆盖index.html替换og标签
  • 提供静态html页面(不确定这是否受角度支持)

我猜你已经使用像ngx-meta这样的东西来添加og标签了吗?

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

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

构建时的角度通用预渲染

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

替代解决方案

使用代理预呈现Angular以提供OG标记

如果您希望避免在构建过程中实现服务器端/预呈现(设置角度通用有时是不好的结构化应用程序的痛苦。)您可以尝试使用预呈现页面的代理服务。看看例如。 prerender.io

覆盖index.html

将所有请求重定向到覆盖og:标记的脚本。例如。 Using PHP and .htaccess to overwrite og tags这也适用于现代环境。例如。你可以使用cloudfront / api网关和lambda函数。虽然没有见过这样的例子。

Facebook缓存和开放图调试

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

使用Facebook Debugger调试打开的图形缓存并清除facebook opengraph cache

答案 1 :(得分:5)

试试这个(使用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);
});

答案 2 :(得分:3)

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

https://universal.angular.io/

答案 3 :(得分:0)

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

因此,只有在.htaccess的帮助下才能获得动态元内容。

如果要呈现动态内容,请使用.htaccess。

RewriteCond%{HTTP_USER_AGENT} facebookexternalhit / 1.1 | Twitterbot | Pinterest | linkedinbot | WhatsApp | Viber | SkypeUriPreview | Google。*摘要[NC,OR]

有关更多信息:

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

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

答案 4 :(得分:0)

截至2018/19年,如果您的主要目标是SEO(或可能更多的“ SMO”-社交媒体优化-因为Googlebot在评估JavaScript方面做得很好,但大多数社交媒体bot却没有),您的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,而是首先构建一个经典网页...

tl; dr只需查看Rendora和Rendertron,它们可能就是您想要的,并且可以非常轻松快捷地使您到达那里。

答案 5 :(得分:0)

在khushali的回答中加上2美分,这对我提供了一个临时解决方案。

在我的托管服务提供商(Dreamhost)上,仅复制/粘贴时[NC,OR]产生了奇怪的结果。在只有一行的RewriteCond上,我不得不将其写为 RewriteCond … googlebot|yandex|…|…|… [NC] (用每行一个重写RewriteCond也可以,但是在第一行中使用[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://stackoverflow.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>

答案 6 :(得分:0)

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

  1. 从API中获取所需的数据。
  2. 使用og-Tags返回简单的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>