单页应用SEO优化一直以来是一个比较头疼的问题,尤其是在项目上线后进行SEO优化这可能会造成项目代码大量更改或者更换技术框架。我们想得到一个解决方案尽量避免修改已有代码来解决SEO优化问题。
一 方案介绍
当前解决方案可以理解成爬虫流量欺骗,将来自爬虫的访问流量转发到一个代理服务中,代理服务负责将已有的单页应用渲染成静态页面并返回给爬虫进行爬取。拓扑结构图如下图所示:
1.1 方案优点:
- 可以理解为对已有代码零更改,只需在对应页面增加TDK设置即可;
- 对客户访问没有影响,负载层进行分流客户流量永远访问不到代理服务;
- 对代理服务器进行开发可以在不更难改原有业务服务源码的的基础上增加TDK相关配置;
1.2 方案缺点:
- 由于是预渲染--->展示过程所以页面延时会增加,需要进行相应的缓存优化避免网站被打低分影响SEO推荐;
- Nginx层分辨爬虫流量规则需要根据爬虫技术更新进行调整,如果使用ip过滤方式怎需要记录相关搜索引擎的服务器ip列表;
二 如何实现
当前解决方案最为核心的技术问题就是如何在服务端将单页应用按需求渲染出来,并保证渲染出来的页面内容与浏览器中查看的内容是相同的。是否有一种方案可以模拟浏览器的能力将页面内容在本地渲染?
Chrome无头浏览器可以做到,在不需要打开任何窗体的情况下将web页面渲染到本地简单理解就是用chrome打开页面然后点击文件网页另存为到x x.html其实就是这么个过程。
2.1 命令方式使用
以mac系统为例首先本地安装chrome浏览器,命令行执行
alias chrome="/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
配置环境变量让chrmoe命令生效然后执行命令
chrome --headless --disable-gpu --dump-dom https://www.baidu.com
可以看到网页信息打印到控制台,关于Chrome无头浏览器其他能力可进入https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md 查看
2.2 以编程方式使用
Puppeteer是Chrome小组开发的Node库。它提供了高级API来控制无头(或完整)Chrome。它与其他自动测试库(如Phantom和NightmareJS)相似。
Puppeteer可用于轻松获取屏幕截图,创建PDF,浏览页面以及获取有关这些页面的信息。如果您想快速自动化浏览器测试,则建议使用该库。它隐藏了DevTools协议的复杂性,并处理了诸如启动Chrome的调试实例之类的多余任务。
2.2.1 尝试使用
确保本地nodejs环境是正常可使用的,运行如下命令:
mkdir puppeteerdemo
cd puppeteerdemo
npm init
npm install puppeteer --save
touch index.js
然后在index.js中写入如下内容
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.baidu.com');
await page.screenshot({ path: 'example.png' });
console.info(await page.content())
await browser.close();
})();
保存文件运行
node index.js
执行成功后会在项目目录下看到一张名为baidu.png
的图片图片内容为百度首页,并且控制台打印出html页面内容其他api可以在Puppeteer网站查看。
2.2.2 实现代理服务
首先我们先创建一个React框架的单页应用程序,这里使用umijs脚手架创建执行如下命令
yarn create umi demo-web
按照提示进行相关信息选择和填写,然后进入到demo-web目录下执行
yarn run start
执行完成请求http://localhost:8000可以看到umi基础项目页面
右键点击页面选择显示网页源码
可以看到下图:
可以看出当前页面是一个典型的单页应用源码,首页只有一个script引用
打开2.2.1章节创建的 puppeteerdemo项目将index.js内容做如下更改
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:8000');
await page.evaluate(() => { });
console.log(await page.content())
await browser.close();
})();
执行
node index.js
会看到如下截图,可以看出已经得到了渲染后的静态页面。
2.2.3 制作http协议接口输出静态页面
2.2.2章节已经可以输出页面静态内容到控制台上了现在只需要将代码翻译成一个可以对外提供服务器的后端服务项目即可,项目地址https://dreamsleep.coding.net/public/seo/seo-proxy/git/files
2.2.4 分辨流量来源
Nginx增加配置
upstream seo_proxy_server {
server localhost:3000;
}
#在 location / 中添加
if ($http_user_agent ~* "Baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|bingbot|Sosospider|Sogou Pic Spider|Googlebot|360Spider") {
proxy_pass http://seo_proxy_server;
}
以上配置是根据http_user_agent中的值进行流量分辨,如果需要根据IP或其他特征值进行分辨可根据情况增加配置。
写在最后
本文所阐述的解决方案属于一个补救方案,在项目开始时分析是否存在SEO需求,然后采用SSR框架进行前端项目架构搭建这样会在后续的开发中遇到SEO相关需求处理起来便会更加从容一些。