V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
rapospectre
V2EX  ›  程序员

前端渲染与 SEO 优化踩坑 小记

  •  5
     
  •   rapospectre ·
    bluedazzle · 2016-08-30 10:46:51 +08:00 · 18740 次点击
    这是一个创建于 3031 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    SEO ( Search Engine Optimization )搜索引擎优化在搜索引擎时代对于网站来讲意义重大。一个网站,不管是小站还是大站,都离不开搜索引擎的流量导入,所以,做好 SEO 一直都是网站站长\开发者一门重要的功课。

    在网站页面后端渲染的时代,开发者只需要按照规范制作搜索引擎友好的页面便可以快速让搜索引擎收录自己网站的各个页面。

    随着前后端技术的更新,越来越多的前端框架进入开发者们的视野,网站的前后分离架构越来越得到开发者们的喜爱与认可。 后端只提供数据接口、业务逻辑与持久化服务,而视图、控制与渲染则交给前端。 因此,越来越多的网站从后端渲染变成了前端渲染,而由此带来的直接问题就是各大搜索引擎爬虫对于前端渲染的页面( 动态内容 )还无法比较完善的爬取,这就导致了网站的内容无法被搜索引擎收录,直接影响网站流量与曝光度。

    博主的网站从去年五月开始也开始采用了前后分离的构架,使用了 AngularJS 框架搭建了 NewRaPo , 之后又使用 Vue.js 框架进行了整体重构 RaPo3。 无一例外,他们都是基于前端渲染的,然后在此后的一年多时间里,搜索引擎收录的页面都是这样的:

    ( 其他搜索引擎也一样,最早的截图已经找不到了,先拿这个应付一下 )

    快照是这样的:

    而博主实际的网站是这样的:

    和这样的:

    感觉完全被搜索引擎抛弃了好嘛!这东西谁能搜到啊!搜到了谁会点啊!

    为了能让搜索引擎正常的收录博主的网站,于是博主踏上了动态 SEO 优化的踩坑之路:

    1 、 fragment 标签

    首先,博主 Google 到了在动态页面中加入 <meta name="fragment" content="!"> 会告诉爬虫这是个动态内容的页面,然后爬虫会在当前链接后面加上 ?_escaped_fragment_=tag 来获取相应页面的静态版,于是果断盘算在路由里改一下,然后重写一套后端渲染的页面返回给所有带 ?_escaped_fragment_=tag 的链接。

    正当我高兴这个问题这么容易解决的时候突然发现网上资料都表示这个方法只有 Google 的爬虫认可,其他搜索引擎全部没用! wtf ,白高兴一场。

    2 、 PhantomJS

    PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API 。它全面支持 web 而不需浏览器支持,其快速,原生支持各种 Web 标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG 。 PhantomJS 可以用于 页面自动化 , 网络监测 , 网页截屏 ,以及 无界面测试 等

    简单来说就是 PhantomJS 能在服务端解析 HTML 、 js 。

    具体怎么使用,简而言之就是判断爬虫来爬取页面的时候把每个动态页面先让 PhantomJS 跑一遍,然后把得到的静态结果返回给爬虫,具体过程可以参考:用 PhantomJS 来给 AJAX 站点做 SEO 优化

    当然博主看过之后没用采用自己搭 PhantomJS 服务做动态内容优化,主要因为:

    1. 爬虫每访问一个页面就要让 PhantomJS 渲染一次,相当于爬虫访问一次实际服务器要响应两次,第一次响应爬虫,第二次响应 PhantomJS 自己,这种方式不仅浪费资源,而且并不优雅;
    2. PhantomJS 对于新的前端技术兼容性会存在问题,可能会出现渲染失败的情况;
    3. 渲染页面无缓存,每访问一次就重新渲染一次,会造成网站响应速度变慢。

    3 、 Prerender.io

    Prerender.io 是一个基于 PhantomJS 开发的专为动态页面 SEO 提供静态页面渲染的在线服务,基本上解决了自己搭建 PhantomJS 服务所遇到的问题,网站配置 Prerender.io 后 Prerender 将会直接取代网站后端对爬虫请求进行响应,将提前渲染好的动态页面直接返回给爬虫。

    具体配置:

    1. 注册 Prerender.io 账号,免费用户可以渲染 250 个页面,对于博客网站来说足够了;
    2. 安装中间件并设置 token ,博主直接采用了 nginx 配置方案,( Prerender.io 也提供了其他解决方案: https://prerender.io/documentation/install-middleware )博主后端服务器是 uWSGI, 根据 Prerender.io 提供的 nginx.conf 中做如下修改:
    server {
        listen 80;
        server_name www.rapospectre.com;
     
        location @prerender {
            proxy_set_header X-Prerender-Token YOUR_TOKEN;
    		include        uwsgi_params;
            
            set $prerender 0;
            if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
                set $prerender 1;
            }
            if ($args ~ "_escaped_fragment_") {
                set $prerender 1;
            }
            if ($http_user_agent ~ "Prerender") {
                set $prerender 0;
            }
            if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
                set $prerender 0;
            }
            
            #resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
            resolver 8.8.8.8;
     
            if ($prerender = 1) {
                
                #setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
                set $prerender "service.prerender.io";
                rewrite .* /$scheme://$host$request_uri? break;
                proxy_pass http://$prerender;
            }
            if ($prerender = 0) {
                uwsgi_pass     127.0.0.1:xxxx;
            }
        }
    }
    

    然后重启服务器,通过 Google Search Console 或其他站长工具提交页面进行爬取检测,可以看到, Prerender.io 成功截取了爬虫请求并进行了渲染:

    嗯,终于解决了嘛,正当博主感叹不容易的时候, Google Search Console 的抓取结果却让人发现然并卵:

    抓取的内容依然是满满的 ${ article.views }} 渲染模版,当时我认为应该是网站缓存的问题,所以没有多想,然而一周后再次测试,情况依旧,回头再看 Prerender 渲染的网页:

    原来压根没起作用!之后又检查了配置和文档,尝试联系了 Prerender.io 的技术支持甚至向 Prerender 的 Github 提了相关 issue ,都没有解决问题。 最后,不得已博主还是放弃了 Prerender 。

    4 、自己搭建后端渲染服务

    Prerender 的方案启发了我,通过判断来访请求的 User-Agent 来让不同的后端服务器进行响应,虽然网上关于 SEO 优化的讨论中明确提到过判断 UA 返回不同页面将会得到搜索引擎的惩罚,但我猜测这只是返回不同内容才会惩罚,如果返回的是相同的内容搜索引擎就不会进行惩罚,区别在于一个是直接通过前端渲染的页面,而另一个则是后端渲染的页面,两个页面渲染出的内容基本相同,那么搜索引擎就不会发现。

    自己动手,丰衣足食,有了想法立即验证,首先把网站代码中前端渲染的部分改为后端渲染,然后 push 到一个新的分支,博主网站修改十分简单,大概只修改了 50 行不到的代码就完成了需求: RaPo3-Shadow

    接着将后端渲染代码部署到服务器,然后假设用 uWSGI 将它跑在 11011 端口, 此时前端渲染的代码由 uWSGI 假设跑在 11000 端口;

    最后修改 nginx 配置文件 nginx.conf :

    server {
        listen 80;
        server_name www.rapospectre.com;
     
        location @prerender {
    		include        uwsgi_params;
            
            set $prerender 0;
            if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
                set $prerender 1;
            }
            #resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
            resolver 8.8.8.8;
     
            if ($prerender = 1) {
    			uwsgi_pass     127.0.0.1:11011;
            }
            if ($prerender = 0) {
                uwsgi_pass     127.0.0.1:11000;
            }
        }
    }
    

    就是通过 UA 判断爬虫,如果是则转发给 11011 端口,不是就转发给 11000 端口,当然两个端口返回的页面基本是相同的,所以也就不用担心会被搜索引擎惩罚了。

    通过以上配置后,动态页面的 SEO 问题终于得到了解决,反应最快的是 Google ,第二天就爬取并更新到了搜索引擎:

    接着 360 搜索:

    然后其他没有提交过网址的搜索引擎也分分收录了网站:

    ( bing 没有收录正常的页面应该是由于 nginx.conf 里没有加入 bing 爬虫 UA 的缘故 )

    当然,不知道什么原因百度过了两个多月还是没有收录,在站长工具中提交网页甚至申诉都没有收录新的网页。没错,开头那个用来应付一下的图片就是百度的结果,刚截的。

    我该说幸好没更新,否则找不到以前的例子了嘛,哈哈哈哈。

    5 、最后一点猜想

    通过博主自己搭建后端渲染服务已经解决了动态页面 SEO 优化问题,但博主还有一个可行办法的猜想,不知是否可行,写在这里如果看了以上办法都不好用的朋友可以试试。

    canonical 标签是 Google 、雅虎、微软等搜索引擎一起推出的一个标签,它的主要作用是用来解决由于网址形式不同内容相同而造成的内容重复问题。 这个标签的制定时间已经很久了,所以现在主流的搜索引擎都支持这个标签,它原本的作用是将 url 不同但内容相同的网址权重全部集中到其中一个网址上,这样可以避免大量重复内容网页被搜索引擎收录,举个例子:

    http://www.rapospectre.com/archives/1
    http://www.rapospectre.com/archives/1?comments=true
    http://www.rapospectre.com/archives/1?postcomment=true
    

    这三个网址的内容其实完全一样,为了避免重复收录和分权,在原始网页加上 <link rel="canonical" href="&lt;a href=" <a="" href="http://www.rapospectre.com/archives/1" rel="nofollow">http://www.rapospectre.com/archives/1" "="" rel="nofollow">http://www.rapospectre.com/archives/1' /> 这样其他重复网页将被看作都是 http://www.rapospectre.com/archives/1

    那么假设对于动态网页 http://www.rapospectre.com/dynamic/1,我们编写他的静态网页:http://www.rapospectre.com/static/1, 然后在 http://www.rapospectre.com/dynamic/1 的页面内加入: , 这样是否也能达到动态 SEO 优化的目的呢?

    总结

    随着前端渲染的页面越来越多,动态页面的 SEO 优化逐渐进入人们的视野,博主将自己动态页面的 SEO 优化经历写出,希望能帮到其他注意到这个领域或遇到相同问题的人,提供一些思路。 谢谢。

    原文地址

    作者:rapospectre

    33 条回复    2016-11-30 17:55:52 +08:00
    leer
        1
    leer  
       2016-08-30 11:43:26 +08:00
    我来赞一个
    billytom
        2
    billytom  
       2016-08-30 11:46:03 +08:00
    我也来赞一个
    master
        3
    master  
       2016-08-30 11:48:39 +08:00
    搜索引擎拿百度做例子 那肯定坑是众所周知的呀
    谷歌的抓取测试工具 当然是显示抓取请求的原始结果 但这不代表 Google 不执行 JS 的呀
    顺带现在这几个主流的框架都有自带保留状态的服务端预渲染 比起这种单纯把 JS 执行后输出的 HTML 作为结果输出的方案要好不少 当然 如果楼主拿 MVVM 框架只用来做 View 展示没有交互 那就当我什么都没说
    chloerei
        4
    chloerei  
       2016-08-30 11:50:05 +08:00
    在这个场景,前段渲染的意义在哪里?
    chloerei
        5
    chloerei  
       2016-08-30 11:50:31 +08:00
    @chloerei 更正:前端
    learnshare
        6
    learnshare  
       2016-08-30 11:54:24 +08:00
    @chloerei 是的,博客并不需要前端渲染,可以后端渲染成静态页输出。
    imnpc
        7
    imnpc  
       2016-08-30 11:59:01 +08:00
    赞一个 然后楼主的图片开梯子都慢得要命
    rapospectre
        8
    rapospectre  
    OP
       2016-08-30 12:13:03 +08:00
    @master 没做优化前 Google 同样也不能抓到正常的内容,只是当时的截图都遗失了,只好拿现在的百度的结果举个例子。 Google 本身的确能执行一些 js 但是还要考虑其他搜索引擎呀。 框架本身的服务端 预渲染我没接触过,是要需要 node 做中间层吧? 这个方案倒是应该也可以解决 SEO 优化问题。
    rapospectre
        9
    rapospectre  
    OP
       2016-08-30 12:19:16 +08:00
    @chloerei 主要是为了尝试前后分离的构架,反正我自己的网站怎么折腾都不需要向上级申报,所以自然会拿博客做一些新的尝试。当然博客最简单就用传统 结构,让后端渲染 是最方便的。这篇文章也只是给有相关问题的人提供一些思路呀。
    rapospectre
        10
    rapospectre  
    OP
       2016-08-30 12:22:31 +08:00
    @imnpc 主要是现在图片就放在自己的服务器上,带宽有限,之后我会放到七牛之类的地方,谢谢支持~
    andypinet
        11
    andypinet  
       2016-08-30 13:07:27 +08:00
    要什么 seo 买流量啊
    aivier
        12
    aivier  
       2016-08-30 13:29:40 +08:00
    为什么要在博客上用 Angular 呢?感觉这种东西应该尽量避免才对,除非项目复杂度真的有必要用
    jsq2627
        13
    jsq2627  
       2016-08-30 13:39:58 +08:00
    也在用自建 prerender ,发现有严重内存泄漏问题。。必须定期杀进程重启。
    guyskk
        14
    guyskk  
       2016-08-30 14:05:49 +08:00
    前端渲染用户体验会更好,可以无刷新,按需加载等等,另外前端 MVVM 框架比后端模板写着更爽。

    我的思路是把页面主要的动态内容在服务端预先塞进去,具体就是在文章页面的 html 中塞一个 `$(content)`,服务端把 `$(content)` 替换为文章内容,其他 ajax 不变。
    rapospectre
        15
    rapospectre  
    OP
       2016-08-30 14:06:02 +08:00
    @andypinet SEM 和 SEO 相辅相成岂不更好
    guyskk
        16
    guyskk  
       2016-08-30 14:06:49 +08:00
    @guyskk 目前还在试验
    rapospectre
        17
    rapospectre  
    OP
       2016-08-30 14:10:04 +08:00
    @guyskk 这样也能解决问题,不过这样后端就不是专注做接口了,相当于还是部分后端渲染。前端渲染好处确实很多,很多交互也可以做的更细腻,而且后端可以更专注到业务,可以做到一套接口各平台通用。
    rapospectre
        18
    rapospectre  
    OP
       2016-08-30 14:10:36 +08:00
    @aivier 就是为了折腾嘛,哈哈
    learnshare
        19
    learnshare  
       2016-08-30 14:13:07 +08:00
    @guyskk 我直接前端拉 Markdown 来渲染了,放到 Github Pages 之类的服务商比较舒服。
    guyskk
        20
    guyskk  
       2016-08-30 14:27:58 +08:00
    @learnshare 前端拉 Markdown 是在服务端把 Markdown 塞到 HTML 里面吗?
    TZ
        21
    TZ  
       2016-08-30 14:48:09 +08:00
    真不错,赞
    learnshare
        22
    learnshare  
       2016-08-30 14:49:47 +08:00
    @guyskk https://github.com/LearnShare/wo-markdown-pages 直接 Ajax 加载 Markdown 文件然后渲染,无后端
    allce231
        23
    allce231  
       2016-08-30 14:53:17 +08:00
    需要 SEO 的网站用 angular 简直是在作死
    jin5354
        24
    jin5354  
       2016-08-30 15:04:04 +08:00
    关键词 同构 vue-server
    bdbai
        25
    bdbai  
       2016-08-30 15:27:59 +08:00 via Android
    @guyskk 感觉这种情况下 pjax 跟好一些。
    bdbai
        26
    bdbai  
       2016-08-30 15:28:26 +08:00 via Android
    @guyskk 跟 => 更
    guyskk
        27
    guyskk  
       2016-08-30 15:44:06 +08:00
    @bdbai 用前端 MVVM 框架又要 SEO 确实麻烦, pjax 实际还是后端模板,优势不大,这样不如纯后端渲染+turbolinks
    twilightlu
        28
    twilightlu  
       2016-08-30 20:58:10 +08:00
    谢谢 很有帮助
    garipan
        29
    garipan  
       2016-09-19 10:19:53 +08:00
    kalasoo
        30
    kalasoo  
       2016-09-19 14:07:36 +08:00
    @garipan thx
    zzlettle
        31
    zzlettle  
       2016-11-30 09:28:31 +08:00
    楼主能告诉下,你当初用 vue.js 写页面内容的时候,里面的文章标题和内容都是写在 js 里面,然后再渲染出来的吗?可能我现在想要的恰恰跟你的相反。我网站的一些内容可能会被搜索引擎特别是百度当做敏感词,所以我恰恰不希望把我一些页面的内容给搜索出来。如果能用这个方法把页面内容一些敏感词被百度搜索不到,我可能会用这个方法试试我的页面。
    rapospectre
        32
    rapospectre  
    OP
       2016-11-30 14:42:12 +08:00
    @zzlettle 是的,用动态渲染的方法百度抓不到内容的,不过百度抓不到他就不会收录,所以你只能自传播了
    zzlettle
        33
    zzlettle  
       2016-11-30 17:55:52 +08:00
    @rapospectre 因为我做的是关于翻墙的网站,敏感词被抓取到。网站或者域名会被屏蔽。我根本就不希望被他们抓取到。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5955 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 02:22 · PVG 10:22 · LAX 18:22 · JFK 21:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.