Angular 的优点有很多, 但如果用它来开发网站的话, 就不得不面对它的两大缺点:
首页加载慢
搜索引擎的爬虫获取不到页面内容
由于 Angular 是通过 js 动态生成 dom 并插入到页面中, 搜索引擎默认只能获得页面的标题. 我们可以使用 curl[1] 命令测试一下.
curl http://localhost:17082
可以看到类似这样的内容:
<!doctype html><html lang="en"><head><meta charset="utf-8"><title> 网站的标题 </title><base href="/"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" type="image/x-icon" href="favicon.ico"><link href="styles.d41d8cd98f00b204e980.bundle.CSS" rel="stylesheet"/></head><body style="margin:0px;"><app-root></app-root><script type="text/javascript" src="inline.59a79e692d20ba910ed0.bundle.js"></script><script type="text/javascript" src="polyfills.5d1d5b016f19755a7d41.bundle.js"></script><script type="text/javascript" src="main.a8103efb00c40fad2538.bundle.js"></script></body></html>
整个页面内容只剩下 title 了.
解决方案
目前有三种解决方案:
服务器端渲染 https://angular.cn/guide/universal . 可以一次解决首屏加载慢和 SEO 问题. 解决方案比较复杂, 跨度较大.
页面缓存服务. 目前比较流行的是 https://prerender.io/ . 可以说是目前最简单且可用的 SEO 方案. 本文将重点介绍此方案.
使用 http://phantomjs.org/ 生成静态页, 再配置 nginx 在爬虫访问时提供静态页中的内容. 实测稍微复杂一点的页面用 phantomjs 生成静态页时会报错. 本文将只简单介绍生成首页静态页的方法.
使用 prerender 页面缓存服务
要演练此部分内容, 首先你要把网站用 nginx 部署 [2].
prerender 的原理是, 配置 nginx, 判断出是爬虫来访时, 先将请求页面地址发送给 prerender 服务, 由 prerender 服务渲染出页面内容, 再将页面内容返回给爬虫. prerender 官网提供的服务地址是 http://service.prerender.io/ 我们可以测试一下该服务的效果, 在浏览器中访问:
https://service.prerender.io/https://www.google.com
即可在浏览器中看到由 prerender 服务返回的静态页面内容了.
虽然我们可以直接使用 http://service.prerender.io/ 服务, 但如果你不希望依赖一个随时可能无法访问的服务的话, 可以架设一个自己的 prerender 服务. 或者你希望在本地演练, 还没有公网的 IP 或域名时, 也需要架设自己的 prerender 服务.
架设自己的 prerender 服务 (可选)
首先在 github 上下载 prerender 源代码 https://github.com/prerender/prerender . 下载后执行 "npm install" 安装依赖项, 再执行 "node server.js" 即可开启服务, 默认端口号是 3000. 我们可以使用
http://localhost:3000/https://www.baidu.com
测试一下.
注意: 如果是在 linux 下部署的话, 还必须先安装 chrome 浏览器, 并且不要以 root 身份执行 "node server.js". 想要一直后台运行服务的话, 可以安装 forever:"npm install forever -g", 然后使用 "forever start server.js" 后台运行服务.
修改 nginx 配置文件
这一步是比较复杂和关键的. 好在 prerender 已经给我们写好了 nodejs,ruby on rails,asp.net,nginx,Apache 等 10 多种 web 服务器的配置方案, 例如官方提供的 nginx 配置方式 https://gist.github.com/thoop/8165802 , 但实测此配置文件有一点问题, 直接拿过来用是不好用的. 我对它进行了一些小修改:
- server {
- listen 17082;
- server_name localhost:17082;
- location / {
- root C:\projects\bzbclub-dist;
- index index.html;
- location / {
- try_files $uri @prerender;
- }
- }
- location @prerender {
- #proxy_set_header X-Prerender-Token YOUR_TOKEN;
- 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) {
- rewrite .*/$scheme://$host:$server_port$request_uri? break;
- proxy_pass http://localhost:3000;
- }
- if ($prerender = 0) {
- rewrite .*/index.html; # 注意这里去掉了结尾的 "break", 否则会报 404 错误
- }
- }
- }
修改完配置文件记得要使用命令 "nginx -s reload" 重新加载.
之后我们首先用浏览器访问我们的网站, 测试一下正常访问没有异常. 然后就可以使用 curl[1] 命令模拟百度蜘蛛访问页面了.
curl -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" http://localhost:17082> z:\temp\bzb-baidu.html
之后打开 z:\temp\bzb-baidu.html 应该可以看到由 prerender 服务渲染出的页面内容.
使用 prerender 缓存服务 (可选)
无论使用 prerender 官网上的服务还是自己架设 prerender 本地服务, 它的速度都不快. 虽然 prerender 服务返回的页面内容只给爬虫看, 并不影响用户体验, 但爬虫获得网页内容的响应时间也是影响搜索结果排名的! 聪明的 prerender 在此看到了商机. 我们来试用一下. 首先, 你要把 nginx.conf 中的本地服务改成官方服务 https://service.prerender.io/ . 然后在 prerender 官网 https://prerender.io/ 注册账户, 验证邮件后登录, 默认就会进入 "Cached Pages" 标签页, 里面 "your token:" 后面跟着的就是分配给你的 token. 将此 token 放置到 nginx.conf 中被注释掉的那行 "X-Prerender-Token" 后面并取消注释即可:
- location @prerender {
- proxy_set_header X-Prerender-Token rM9WgVZmgir9bSiE4sGp;
- 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) {
- rewrite .*/$scheme://$host:$server_port$request_uri? break;
- proxy_pass https://service.prerender.io;
- }
- if ($prerender = 0) {
- rewrite .*/index.html; # 注意这里去掉了结尾的 "break", 否则会报 404 错误
- }
- }
修改配置之后使用命令 "nginx -s reload" 重新加载, 然后再次模拟百度蜘蛛访问:
curl -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" http://47.94.88.230> z:\temp\bzb-online.html
刷新 prerend.io 页面, 在 "Cached Pages" 标签页下方的列表中即可看到已被自动缓存了的页面地址. 实测不用缓存的时候要 3,4 秒钟的页面, 缓存后只要 0.2 秒, 效果很理想. 不过免费账户最多只给缓存 250 个页面, 一般也够用了. 如果想要缓存更多页面的话就付费吧, 价格表在 https://prerender.io/pricing , 起步价每月 15 美元可以缓存 20000 个页面. 但需要注意的是, 缓存默认的刷新周期都是 7 天, 免费版也可以自由修改刷新周期, 最短到 1 天. 但付费版则是周期越短费用越高, 例如起步价 15 美元那个, 7 天要 15 美元, 6 天要 17 美元, 5 天要 20 美元, 1 天则要 84 美元.
使用 phantomjs 生成静态页
prerender 已经足够好了, 但如果你想多了解一下 phantomjs 的话, 请接着往下看. 使用此种方案, 需要解决 3 个问题:
如何得知网站有多少需要生成静态页的页面, 生成的静态页文件以何种层级关系存放, 以及如何把访问地址与静态页文件匹配.
如何使用 phantomjs 生成静态页.
如何在爬虫访问时让爬虫获取到静态页中的内容.
我们先忽略前两个问题, 假设现在已经生成了首页的静态页 (可以先通过浏览器保存网页的功能得到首页的静态页), 先解决问题 3.
如何在爬虫访问时让爬虫获取到静态页中的内容
要演练此部分内容, 首先你要把网站用 nginx 部署 [2].
如何判断是爬虫访问还是浏览器访问
爬虫访问时, 会使用特殊的 user agent, 以百度蜘蛛的 UA https://ziyuan.baidu.com/wiki/387 为例, 它会使用 "Mozilla/5.0 (compatible; Baiduspider/2.0; + http://www.baidu.com/search/spider.html )" 这样的 UA, 所以我们可以判断 UA 中含有 "Baiduspider" 字样则意味着是百度蜘蛛来访问了.
如何在百度蜘蛛来访问时返回静态页
先把静态页放置到网站的 /assets/static/ 下, 配置 nginx 的配置文件 nginx.conf:
- location / {
- root C:\projects\bzbclub-dist;
- index index.html index.htm;
- if ( $http_user_agent ~* "Baiduspider"){
- rewrite ^/index.html$ /assets/static/index.html last;
- }
- }
保存配置文件后要使用 nginx -s reload 重新加载网站, 然后使用 curl 命令的 "-A" 参数模拟百度蜘蛛访问首页:
curl -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" http://localhost:17082> z:\temp\bzbclub.html
打开 z:\temp\bzbclub.html 确认是否已经返回了静态页的内容.
如何使用 phantomjs 生成静态页
生成静态页的脚本很简单:
- var fs = require('fs');
- var page = require('webpage').create();
- phantom.outputEncoding = "utf-8";// 指定编码方式
- page.open("http://localhost:17082", function (status) {
- if (status === "success") {
- fs.write('z:\\temp\\index.html', page.content, 'w');
- } else {
- console.log("网页加载失败");
- }
- phantom.exit(0);// 退出系统
- });
将此脚本保存为 "phantom-static-page.js", 在命令行执行此脚本:
phantomjs phantom-static-page.js
附录
[1] curl
在 Windows 下也可以使用 https://curl.haxx.se/download.html#Win64 .
如果遇到 "Failed writing body" 错误, 可以使用
curl http://localhost:17082> z:\temp\test.txt
之后即可看到生成的 "z:\temp\index.html" 静态页文件.
这样的命令将输出写入文本文件中, 再打开文本文件查看内容即可.
[2] nginx 部署
如果你是首次使用 nginx, 有几个地方需要注意.
默认 nginx 不开启 gzip, 需要修改配置文件开启 gzip, 方法是修改 nginx.conf 配置文件的 http 节:
- http {
- gzip on;
- gzip_comp_level 6; # 压缩比例, 比例越大, 压缩时间越长. 默认是 1
- gzip_types text/xml text/plain text/css application/javascript application/x-javascript application/rss+xml; # 哪些文件可以被压缩
- gzip_disable "MSIE [1-6]\."; # IE6 无效
- }
解决刷新二级页面时报 404 错误的问题
如果你不是使用 hash 路由 (即路径中不含 "#"), 当你使用 "在新标签页打开链接" 的方式打开指向新路由的链接, 或者在二级页面上刷新页面时, 都会报 404 错误. 这是因为路由指向的页面实际是不存在的, 是 Angular 虚拟的, 必须经由 index.html 中放置的根路由 (<base href="/">) 来渲染, 所以需要在配置文件 nginx.conf 增加 "tryfile", 把虚拟路径重定向到 index.html:
- server {
- listen 17082;
- location / {
- root C:\projects\bzbclub-dist;
- index index.html index.htm;
- try_files $uri $uri//index.html;
- if ( $http_user_agent ~* "Baiduspider"){
- rewrite ^/index.html$ /assets/static/index.html last;
- }
- }
- }
来源: https://segmentfault.com/a/1190000014596299