由于众所周知的原因,爬虫通常不能抓取到通过js渲染的内容,特别是在前后分离的场景下。下面是一个简单的js动态渲染的例子:
<script>
eval( "var word=" + '"\\u' + (Math.round(Math.random() * 20901) + 19968).toString(16)+'"')
document.write(word);
</script>
例子中动态输出了一个中文字符。为了抓取这些内容,我们通常会使用一些自动化测试工具,如python的selenium。下面也是一个简单的例子:
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
#自定义option来控制chrome的GUI显示
option=webdriver.ChromeOptions()
option.add_argument('headless')
browser = webdriver.Chrome(chrome_options=option)
browser.get('http://localhost/index.html')
#等待1秒后输出,这里可以根据实际情况调整等待时长
wait = WebDriverWait(browser,1)
print(browser.page_source)
这时候得到的html源码就包含了动态渲染的内容了:
<html>
<head>
<script>
eval( "var word=" + '"\\u' + (Math.round(Math.random() * 20901) + 19968).toString(16)+'"')
document.write(word);
</script>
</head>
<body>唘</body>
</html>
这就产生了js动态渲染的一种思路:针对爬虫输出渲染后的HTML代码。对比一下前后分离站点常用的SEO方案:
SSR服务端渲染:
优点:爬虫可以直接访问到渲染完成的页面。
缺点:消耗服务器资源,环境和部署成本增加。因为执行环境的不同,已有项目不容易直接上SSR,需要做很多特殊处理。
静态化:
优点:纯静态网页,不消耗服务器性能。
缺点:不适用动态路由参数,如参数变动需要重新打包
预渲染:
优点:几乎不用改代码,引入一个插件就可以了
缺点:仅适用于少量且不太变化页面的渲染。和静态化一样页面变化需要重新打包。大量页面打包效率低。
服务端浏览器渲染:
优点:与应用本身毫无关系,不需要调整业务代码。
缺点:消耗服务器性能(消耗有点多)
所以结论就是,在性能允许的情况下。优化已有站点的SEO最简单的方式就是服务端开启一个浏览器来主动为爬虫渲染页面。接下来实战:
首先,引入小学一年级就学过的flask框架:
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from flask import *
app=Flask(__name__)
#匹配任意路由 当然也可以使用正则匹配指定内容
@app.route('/<path:path>')
def explorer(path):
option=webdriver.ChromeOptions()
option.add_argument('blink-settings=imagesEnabled=false') #不加载图片, 提升速度
option.add_argument('--headless')
browser = webdriver.Chrome(chrome_options=option)
#为了拿到完整参数的临时解决方案
browser.get(request.full_path[1:])
#暂且等待5秒
wait = WebDriverWait(browser,5)
result = browser.page_source
browser.close()#关闭浏览器
return result
if __name__ == '__main__':
app.run(debug=True, threaded=True, host='0.0.0.0', port=5000,use_reloader = False)
这时候我们访问 http://localhost:5000/http://localhost/index.html就可以得到渲染完成的页面了。这时候我们只需要将web服务接收到的爬虫访问转发到falsk服务上,以nginx为例:
#判断爬虫特殊的请求头 百度|字节跳动|bing|神马|搜狗|360
if ($http_user_agent ~* "Baiduspider|Bytespider|bingbot|YisouSpider|Sogou web spider|360Spider" ) {
#转发到flask 原地址放在后面作为参数
proxy_pass http://127.0.0.1:5000/$scheme://$server_addr$request_uri;
}
然后我们使用postman模拟爬虫访问做个测试, User-Agent 写入一个爬虫的标识:

可以看到,输出了动态渲染的内容,不过针对爬虫特殊渲染导致和实际用户访问不一致可能会被搜索引擎判断为作弊哈。最后说一句,如果新构建大型网站还是考虑SSR吧。