前言
目前SPA项目的SEO方案有:
- 客户端渲染(CSR):对没有执行js能力的爬虫来说不友好
- 服务端渲染(SSR):改造和维护成本高
- 构建时预渲染:利用构建工具提前构建不同路由对应的页面,问题是没法动态变更
- 服务端动态渲染:利用user-agent判断如果是爬虫则使用无头浏览器渲染页面再返回
对比来看服务端动态渲染可以以比较小的代价换取比较好的结果,而且也有现成的解决方案:github Rendora
Rendora 简介
Rendora是一个动态渲染器,主要为web爬虫提供零配置服务器端渲染,以便轻松地改进在React.js、Vue.js、Angular.js等现代Javascript框架中开发的网站的SEO。Rendora完全独立于前端和后端堆栈。
Rendora可以被视为一个反向HTTP代理服务器,位于后端服务器(例如Node.js/Express.js、Python/Django等)和潜在的前端代理服务器(例如nginx、traefik、apache等)之间,甚至可以直接连接到外部世界,实际上除了按原样传输请求和响应之外,它什么都不做,除非根据配置。在这种情况下,Rendora指示无头Chrome实例请求并呈现相应的页面,然后将服务器端呈现的页面返回给客户端(即前端代理服务器或外部世界)。这个简单的功能使Rendora成为一个强大的动态渲染器,而无需实际更改前端和后端代码。
简单理解就是将页面请求代理到Rendora,Rendora内部通过user-agent判断是否是爬虫机器人请求,如果不是爬虫请求则代理到Backend(配置文件配置的服务器地址),如果是爬虫请求,则使用无头浏览器渲染页面,再把渲染好的html返回
Rendora 主要特征
- 前端和后端代码无需更改
- 基于用户代理和路径的筛选器
- 用Golang编写的单快速二进制
- 多种缓存策略
- 支持异步页面
- Prometheus metrics
- 选择您的配置系统(YAML、TOML或JSON)
- 开箱即用
环境准备
这里仅以 Windows 环境来搭建,关于Windows下安装Docker环境准备可以参照这里Docker 部署npm私仓 Verdaccio/环境准备
基于Docker安装Rendora
Rendora官方文档已经给出详细的安装方式,这里不再赘述
拉取Rendora相关镜像
docker pull rendora/chrome-headless
docker pull rendora/rendora
成功结果如下,Images有对应镜像:
- 如果拉取过慢或者出现拉取不成功提示(如:
Unable to find image) 可以在 Docker Engine 新增国内镜像设置再重试
"registry-mirrors": [
"https://registry.docker-cn.com"
]
构建 chrome-headless 容器
如下图操作,可以在 Optional settings 修改设置,我这里只修改了容器名称为 chrome-headless
成功后,容器列表会有对应容器,点击运行按钮启动即可
构建 Rendora 容器
在构建 Rendora 容器前要先准备测试项目,也可以用当前开发的SPA项目进行测试
新建一个项目
- 以
http-server做个简单服务器,全局安装http-server
npm i -g http-server
- 新建一个文件夹,创建
index.html文件并写入以下内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>rendora</title>
</head>
<body>
<div id="root"></div>
<script>
document.getElementById("root").innerText = "无头浏览器渲染";
</script>
</body>
</html>
- 启动服务
# 进入上一步新建的文件夹内启动终端执行以下命令
$ http-server
启动结果如下:
Available on:
http://192.168.204.128:8080
http://127.0.0.1:8080
Hit CTRL-C to stop the server
新增 Rendora 配置文件
新建 CONFIG_FILE.yaml 配置文件,写入以下配置,注意 target.url 需要取上述服务器地址
listen:
address: 0.0.0.0
port: 3001
cache:
type: none
target:
# 上面个服务器地址
url: "http://192.168.204.128:8080"
backend:
# 上面个服务器地址
url: "http://192.168.204.128:8080"
headless:
waitAfterDOMLoad: 500
timeout: 10
internal:
url: http://localhost:9222
blockedURLs:
[
"*.png",
"*.jpg",
"*.jpeg",
"*.webp",
"*.conf",
"*.gif",
"*.woff2",
"*.svg",
"*.woff",
"*.ttf",
"https://www.youtube.com/*",
"https://www.youtube1.com/*",
"https://www.google-analytics.com/*",
]
output:
minify: true
filters:
userAgent:
defaultPolicy: blacklist
exceptions:
keywords:
- bot
- Googlebot
- slurp
- bing
- crawler
- twitter
paths:
defaultPolicy: whitelist
exceptions:
prefix:
- /static
- /users/
server:
enable: true
基于上述配置文件构建 Rendora 容器,为了方便这里直接在终端使用命令构建,使用 -v 指令指定映射,我的 CONFIG_FILE.yaml 文件在D盘docker文件夹下,这里直接构建并启动
docker run --name=rendora --net=host -v /D/docker/CONFIG_FILE.yaml:/etc/rendora/config.yaml rendora/rendora
成功后会在容器列表看到 rendora 容器
启动 Rendora 注意事项
- 先确保
chrome-headless已经启动,否则rendora会因连接不上chrome-headless而导致启动失败 - 如果是使用映射配置文件启动,需要保证配置文件存在且内容及格式正确,否则也可能导致失败,不过构建时也会有提示
测试
- 正常请求测试,进入容器终端执行以下命令
curl http://127.0.0.1:3001
结果如下,得到的是html原本的样子
- 爬虫请求测试,修改刚才的命令如下
curl -H "User-Agent: Googlebot" http://127.0.0.1:3001
结果如下,经过无头浏览器的渲染,js已经执行了
Rendora 配置文件核心内容讲解
详细的配置说明请看官方文档
listen
这个很容易理解就是 Rendora 自身地址以及监听端口
filters 请求过滤器
| filters 参数 | 可选值/类型 | 含义 |
|---|---|---|
| userAgent.defaultPolicy | whitelist and blacklist | 匹配策略,有白名单和黑名单两种模式 |
| userAgent.exceptions | - | 只有符合规则的才通过 |
| userAgent.exceptions.keywords | String[] | 只要user-agent包含keywords中的字符就符合 |
| userAgent.exact | - | user-agent完全匹配该配置内容才符合 |
target & backend 目标地址和原地址
| 参数 | 含义 |
|---|---|
| target.url | 发送给无头浏览器的地址 |
| backend.url | 服务器原地址 |
由于 Rendora 本身也是个代理服务器,其核心工作是根据 filters 区分请求的用户,通过过滤器的则通过无头浏览器获取,否则代理到原服务,而无头浏览器请求的地址实际上也是原服务,如果 Rendora 服务和原服务在一个局域网(一般来讲是在一起的),这里可以配置成原服务(ip形式),可减少域名解析等网络链路,也就是说target.url和backend.url是同一个地址时速度比较快
headless 无头浏览器配置
| headless参数 | 含义 | 备注 |
|---|---|---|
| waitAfterDOMLoad | 页面DOMLoad事件触发后生成快照前等待渲染的时间 | DOMLoad事件触发后还需要等待无头浏览器执行渲染,这个时间就是渲染时间,可根据自己网站情况配置 |
| internal.url | 无头浏览器的请求的地址 | 这里是 headless 服务地址,Rendora 启动时会检查该地址是否可连通 |
| blockedURLs | 无头浏览器渲染时获取所有请求黑名单 | 由于目标是渲染后的html页面,像图片、css、埋点SDK之类的js可能不是必须的,所以不需要加载,不过在屏蔽对应js资源时需要做好容错处理,防止因为某些资源未加载从而导致js执行失败并最终导致页面渲染失败,另外一些同步资源超时或404也可能影响 DOMLoad 事件触发或者影响生成快照 |
总结
在上面的测试中就可以体验到,走无头浏览器渲染的时间一定稍微久一点,因为启动无头浏览器包括之后的请求和渲染都比正常直接请求页面多一些时间,不过对于爬虫来说还可以接受,同时,还可以使用缓存策略,整体来说 Rendora 非常的简单易用,真正做到开箱即用
如果对于 Rendora 还有什么不满的,可以参考Headless Chrome Google 官方 Demo自行开发一套代理工具