背景

单页应用没法做 SEO,vue 官方提供了 SSR(服务端渲染)、Nuxt.js 等方案,但都要修改项目原来的代码来适配,对于已经存在的项目来说很不适合

根据爬虫和用户的特性,爬虫需要的是有尽量多的页面信息,并不关心页面能不能操作,而用户需要的是完整可操作的页面,所以我们可以根据爬虫和用户的这些区别,服务端返回不同的页面

针对爬虫,返回预先渲染过的页面,针对用户,返回原本的单页应用页面

预渲染可以使用 https://github.com/tvanro/prerender-alpine

prerender-alpine

prerender-alpine 使用 docker 启动,起来之后监听 3000 端口,将需要预渲染的 url 传给它,就可以返回一个这个页面渲染好的 html 文本

curl httl://localhost:3000/render?url=https://www.test.com/a/b

搭建 prerender-alpine

version: "3"
services:
    prerender-alpine:
        image: tvanro/prerender-alpine:7.0.1
        container_name: prerender-alpine
        ports:
           - 3000:3000
        environment:
           - CACHE_MAXSIZE=1000
           - CACHE_TTL=6000
           - MEMORY_CACHE=1
        network_mode: "bridge"

配置后端代理转发

搜索引擎的爬虫都有特殊的请求头,我们可以根据每个请求的请求头来区分是普通用户还是搜索引擎爬虫

如果是普通用户访问,则走原来的逻辑,如果是搜索引擎爬虫,则反向代理到 prerender-alpine

本次使用 nginx 配置

原本的配置如下:

server{
    listen              80;
    server_name         test.com;

    # 修改上传文件大小限制
    client_max_body_size 100m;
    client_body_buffer_size 50m;
    
    # 重写规则
    # api前缀,转发到后端接口服务
    location ^~ /api/ {
        proxy_pass      http://127.0.0.1:82/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
    # 其他路径,转发到前端页面服务
    location / {
        proxy_pass      http://127.0.0.1:81/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

修改后配置如下:

server{
    listen              80;
    server_name         test.com;

    # 修改上传文件大小限制
    client_max_body_size 100m;
    client_body_buffer_size 50m;

    # 定义搜索引擎爬虫的 User-Agent
    map $http_user_agent $is_bot {
      default 0;
      ~*Googlebot 1;
      ~*Bingbot 1;
      ~*Slurp 1;
      ~*DuckDuckBot 1;
      ~*Baiduspider 1;
      ~*YandexBot 1;
      ~*Sogou 1;
      ~*Exabot 1;
      ~*facebot 1;
      ~*ia_archiver 1;
    }    

    # 重写规则
    # api前缀,转发到后端接口服务
    location ^~ /api/ {
        proxy_pass      http://127.0.0.1:82/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # 其他路径,转发到前端页面服务
    location / {
        # 如果是爬虫,则转发到 prerender-alpine
        if ($is_bot) {
            # prerender-alpine是使用docker启动,无法通过127.0.0.1访问前端页面
            # 这里的172.17.0.1是宿主机docker的网关ip,能被容器内访问到
            # set_escape_uri 是将字符串进行url编码,这个命令需要nginx安装 set-misc-nginx-module 这个插件
            set_escape_uri $encoded_bot_url http://172.17.0.1:81$request_uri;
            
            # 转发到prerender-alpine
            proxy_pass      http://127.0.0.1:3000/render?url=$encoded_bot_url;
            break;
        }

        # 非爬虫,继续走原逻辑
        proxy_pass      http://127.0.0.1:81/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

搜索引擎爬虫 User-Agent 包含的关键字

  • Googlebot:Googlebot

  • Bingbot:Bingbot

  • Yahoo Slurp:Slurp

  • DuckDuckGo:DuckDuckBot

  • Baiduspider:Baiduspider

  • YandexBot:YandexBot

  • Sogou:Sogou

  • Exalead:Exabot

  • Facebook:facebot

  • Internet Archive:ia_archiver