7.2-Splash的使用
Splash 是一个 JavaScript 渲染服务,是一个带有 HTTP API 的轻量级浏览器,同时它对接了 Python 中的 Twisted 和 QT 库。利用它,我们同样可以实现动态渲染页面的抓取。
利用 Splash 我们可以实现如下功能:
- 异步方式处理多个网页渲染过程
- 获取渲染后的页面的源代码或截图
- 通过关闭图片渲染或者使用 Adblock 规则来加快页面渲染速度
- 可执行特定的 JavaScript 脚本
- 可通过 Lua 脚本来控制页面渲染过程
- 获取渲染的详细过程并通过 HAR(HTTP Archive)格式呈现
接下来我们来了解一下它的具体用法。
在开始之前,请确保已经正确安装好了 Splash 并可以正常运行服务。如果没有安装,可以参考第 1 章。
首先,通过 Splash 提供的 Web 页面来测试其渲染过程。例如,我们在本机 8050 端口上运行了 Splash 服务,打开 http://localhost:8050/ 即可看到其 Web 页面,如图 7-6 所示。

图 7-6 Web 页面
在右侧呈现的是一个渲染示例,我们可以看到在上方有一个输入框,默认是 http://google.com,我们在这里换成百度测试一下,将内容更改为:https://www.baidu.com,然后点击按钮,开始渲染,结果如图 7-7 所示:

图 7-7 运行结果
可以看到,网页的返回结果呈现了渲染截图、HAR 加载统计数据、网页的源代码。
通过 HAR 的结果可以看到,Splash 执行了整个网页的渲染过程,包括 CSS、JavaScript 的加载等过程,呈现的页面和我们在浏览器中得到的结果完全一致。
那么,这个过程由什么来控制呢?重新返回首页,可以看到实际上是有一段脚本,内容如下:
function main(splash, args)
assert(splash:go(args.url))
assert(splash:wait(0.5))
return {html = splash:html(),
png = splash:png(),
har = splash:har(),}
end
这个脚本实际上是用 Lua 语言写的脚本。即使不懂这个语言的语法,但从脚本的表面意思,我们也可以大致了解到它首先调用 go 方法去加载页面,然后调用 wait 方法等待了一定时间,最后返回了页面的源码、截图和 HAR 信息。
到这里,我们大体了解了 Splash 是通过 Lua 脚本来控制了页面的加载过程的,加载过程完全模拟浏览器,最后可返回各种格式的结果,如网页源码和截图等。
接下来,我们就来了解 Lua 脚本的写法以及相关 API 的用法。
Splash 可以通过 Lua 脚本执行一系列渲染操作,这样我们就可以用 Splash 来模拟类似 Chrome、PhantomJS 的操作了。
首先,我们来了解一下 Splash Lua 脚本的入口和执行方式。
首先,来看一个基本实例:
function main(splash, args)
splash:go("http://www.baidu.com")
splash:wait(0.5)
local title = splash:evaljs("document.title")
return {title=title}
end
我们看到它返回了网页的标题,如图 7-8 所示。这里我 们通过 evaljs 方法传入 JavaScript 脚本,而 document.title 的执行结果就是返回网页标题,执行完毕后将其赋值给一个 title 变量,随后将其返回。

图 7-8 运行结果
注意,我们在这里定义的方法名称叫作 main 。这个名称必须是固定的,Splash 会默认调用这个方法。
该方法的返回值既可以是字典形式,也可以是字符串形式,最后都会转化为 Splash HTTP Response,例如:
function main(splash)
return {hello="world!"}
end
这样即返回了一个字典形式的内容。
function main(splash)
return 'hello'
end
这样即返回了一个字符串形式的内容,同样是可以的。
Splash 支持异步处理,但是这里并没有显式指明回调方法,其回调的跳转是在 Splash 内部完成的。示例如下:
function main(splash, args)
local example_urls = {"www.baidu.com", "www.taobao.com", "www.zhihu.com"}
local urls = args.urls or example_urls
local results = {}
for index, url in ipairs(urls) do
local ok, reason = splash:go("http://" .. url)
if ok then
splash:wait(2)
results[url] = splash:png()
end
end
return results
end
运行后的返回结果是 3 个站点的截图,如图 7-9 所示:

图 7-9 运行结果
在脚本内调用的 wait 方法类似于 Python 中的 sleep 方法,其参数为等待的秒数。当 Splash 执行到此方法时,它会转而去处理其他任务,然后在指定的时间过后再回来继续处理。
这里值得注意的是,Lua 脚本中的字符串拼接和 Python 不同,它使用的是.. 操作符,而不是 +。如果有必要,可以简单了解一下 Lua 脚本的语法,详见 http://www.runoob.com/lua/lua-basic-syntax.html。
另外,这里做了加载时的异常检测。go 方法会返回加载页面的结果状态,如果页面出现 4xx 或 5xx 状态码,ok 变量就为空,就不会返回加载后的图片。
我们注意到,前面例子中 main 方法的第一个参数是 splash,这个对象非常重要,它类似于 Selenium 中的 WebDriver 对象,我们可以调用它的一些属性和方法来控制加载过程。接下来,先看下它的属性。
该属性可以获取加载时配置的参数,比如 URL,如果为 GET 请求,它还可以获取 GET 请求参数;如果为 POST 请求,它可以获取表单提交的数据。Splash 也支持使用第二个参数直接作为 args,例如:
function main(splash, args)
local url = args.url
end
这里第二个参数 args 就相当于 splash.args 属性,以上代码等价于:
function main(splash)
local url = splash.args.url
end
这个属性是 Splash 的 JavaScript 执行开关,可以将其配置为 true 或 false 来控制是否执行 JavaScript 代码,默认为 true。例如,这里禁止执行 JavaScript 代码:
function main(splash, args)
splash:go("https://www.baidu.com")
splash.js_enabled = false
local title = splash:evaljs("document.title")
return {title=title}
end
接着我们重新调用了 evaljs 方法执行 JavaScript 代码,此时运行结果就会抛出异常:
{
"error": 400,
"type": "ScriptError",
"info": {
"type": "JS_ERROR",
"js_error_message": null,
"source": "[string \"function main(splash, args)\r...\"]",
"message": "[string \"function main(splash, args)\r...\"]:4: unknown JS error: None",
"line_number": 4,
"error": "unknown JS error: None",
"splash_method": "evaljs"
},
"description": "Error happened while executing Lua script"
}
不过一般来说我们不用设置此属性开关,默认开启即可。
此属性可以设置加载的超时时间,单位是秒。如果设置为 0 或 nil(类似 Python 中的 None),代表不检测超时。示例如下:
function main(splash)
splash.resource_timeout = 0.1
assert(splash:go('https://www.taobao.com'))
return splash:png()
end
例如,这里将超时时间设置为 0.1 秒。如果在 0.1 秒之内没有得到响应,就会抛出异常,错误如下:
{
"error": 400,
"type": "ScriptError",
"info": {
"error": "network5",
"type": "LUA_ERROR",
"line_number": 3,
"source": "[string \"function main(splash)\r...\"]",
"message": "Lua error: [string \"function main(splash)\r...\"]:3: network5"
},
"description": "Error happened while executing Lua script"
}
此属性适合在网页加载速度较慢的情况下设置。如果超过了某个时间无响应,则直接抛出异常并忽略即可。
此属性可以设置图片是否加载,默认情况下是加载的。禁用该属性后,可以节省网络流量并提高网页加载速度。但是需要注意的是,禁用图片加载可能会影响 JavaScript 渲染。因为禁用图片之后,它的外层 DOM 节点的高度会受影响,进而影响 DOM 节点的位置。因此,如果 JavaScript 对图片节点有操作的话,其执行就会受到影响。
另外值得注意的是,Splash 使用了缓存。如果一开始加载出来了网页图片,然后禁用了图片加载,再重新加载页面,之前加载好的图片可能还会显示出来,这时直接重启 Splash 即可。
禁用图片加载的示例如下:
function main(splash, args)
splash.images_enabled = false
assert(splash:go('https://www.jd.com'))
return {png=splash:png()}
end
这样返回的页面截图就不会带有任何图片,加载速度也会快很多。
此属性可以控制浏览器插件(如 Flash 插件)是否开启。默认情况下,此属性是 false,表示不开启。可以使用如下代码控制其开启和关闭:
splash.plugins_enabled = true/false
通过设置此属性,我们可以控制页面上下或左右滚动。这是一个比较常用的属性,示例如下:
function main(splash, args)
assert(splash:go('https://www.taobao.com'))
splash.scroll_position = {y=400}
return {png=splash:png()}
end
这样我们就可以控制页面向下滚动 400 像素值,结果如图 7-10 所示。

图 7-10 运行结果
如果要让页面左右滚动,可以传入 x 参数,代码如下:
splash.scroll_position = {x=100, y=200}