使用headless解决SEO问题,原理是在服务器使用浏览器渲染好页面后返回给有益的蜘蛛爬虫,代码也没有服务端客户端的区别。渲染过程可能会比较消耗服务器资源,建议单独使用一台服务器搭建,我没有做压力测试,如果有人做了也希望分享我一下。
1、安装依赖
# 安装 puppeteercnpm install puppeteer# puppeteer依赖,如果已安装谷歌浏览器可略过yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64yum install xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-miscyum update nss -y
2、server.js
const http = require('http');const puppeteer = require('puppeteer');let conf = { // 是否使用https访问API useHttps: false, // 是否检测IP,防止伪造UA checkIp: false, // 可以访问的蜘蛛IP地址 allowIpList: [], launchOption: { args: ['--no-sandbox', '--disable-setuid-sandbox'] }};/** * 获取IP地址 * * @param req * @returns {*|string} */function getIp (req) { return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress || '';}/** * 检测ip是否合法 * * @param ip * @returns {boolean} */function checkIP(ip) { //也可以使用别的规则校验 return conf.checkIp ? conf.allowIpList.includes(ip) : true;}// 载入浏览器puppeteer.launch(conf.launchOption).then(async browser => { const server = http.createServer(); server.on('request', function(request, response){ let url = (conf.useHttps ? 'https://' : 'http://') + request.headers.host + request.url; let ip = getIp(request); // 检测ip地址 if (checkIP(ip)) { browser.newPage().then(async page => { // 访问url await page.goto(url); let content= await page.content(); // 关闭页面,返回数据 await page.close(); response.end(content); }) } else { response.statusCode = 404; response.end("404 NOT Found"); } }) server.on('close', function(){ browser.close(); }) server.listen(3000, '127.0.0.1', function () { console.log('puppeteer server listening on port 3000!'); });});
这里我选择先打开浏览器再启动服务;还有一种方式时先启动服务,接受到请求再打开浏览器处理请求,但我觉得这样每个请求都要打开关闭一次浏览器,有点浪费资源。不知道我这个想法是否正确,欢迎指正。
3、nginx配置
location / { # 蜘蛛爬虫处理 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; if ($http_user_agent ~* "spider|bot") { proxy_pass http://127.0.0.1:3000; } try_files $uri $uri/ @router;}location @router { rewrite ^(.*)$ /index.html last;}
4、hosts配置
配置host,从内网访问API
127.0.0.1 api.domain.com
5、其他
puppeteer 并不是最优解决方案,甚至是很差的解决方案。需要最新的 vue 3 的服务端渲染方案,可以看我的另一篇博文 Vue 3 + Vite + SSR