Splash是一个JavaScript渲染服务,是一个带有HTTP API的轻量级浏览器,同时它对接了Python中的Twisted和QT库。利用它,我们同样可以实现动态渲染页面和抓取。
我们要进行Splash 的安装,安装Splash我们要先安装Docker,我们只需从https://docs.docker.com/docker-for-windows/install/下载最新的Docker for Windows安装包即可,下载过程中我遇到了许多问题下面我就一一帮大家解决
我第一次用docker安装splash出现这种错误,这是因为我们的设置的DOCKER_HOST的设置连接不上去,后来我去百度它给我的解决办法是输入【docker-machine env default】,然后我输了还是不行,还是会继续报错,如下所示:
意思就是说Host连接不上去了,这时候我们就要重启下default,输入命令【docker-machine restart default】,如下所示:
然后再输入命令【docker-machine env --shell cmd default】,如下所示:
然后把下面的内容复制下就可以了,如下所示:
这时候再输入命令【docker version】,我们就可以显示成功了,如下所示:
再运行命令安装Splash即可安装成功如下所示:
然后输入命令【docker-machine ip default]会显示ip地址,将下面显示的地址复制到网页输出,如下所示:
这样Splash即可安装成功了
如上图右侧,呈现的是一个渲染示例。可以看到,上方有一个输入框,我们点击render me按钮开始渲染,可以看到,网页的返回结果呈现了渲染截图,HAR加载统计数据、网页的源代码。
这个过程是由一段脚本构成,这个脚本实际上是用Lua语言写的脚本,下面我们会介绍。
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
返回了网页的标题,这里我们通过evaljs()方法传入JavaScript脚本,而document.title的执行结果就是返回网页标题,执行完成后将其赋值给一个title变量,随后将其返回。
我们在这里定义的方法名称叫做main()。这个名称必须是固定的,Splash会默认调用这个方法。
该方法的返回值既可以是字典形式,也可以是字符串形式,最后都会转化为Splash HTTP Response。
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个站点
类似于Selenium中的WebDriver对象,我们可以调用它的一些属性和方法来控制加载过程。
该属性可以获取加载时配置的参数,比如URL,如果为GET请求,它还可以获取GET请求参数;如果为POST请求,它可以获取表单提交的数据。Splash也支持使用第二个参数直接作为args,例如:
function main(splash, args)
local url=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代码,此时运行结果就会抛出异常:
HTTP Error 400 (Bad Request)
Type: ScriptError -> LUA_INIT_ERROR
Error happened while executing Lua script
[string "function main(splash, args)
..."]:3: function arguments expected near '='
{
"type": "ScriptError",
"error": 400,
"info": {
"source": "[string \"function main(splash, args)\r...\"]",
"line_number": 3,
"message": "[string \"function main(splash, args)\r...\"]:3: function arguments expected near '='",
"type": "LUA_INIT_ERROR",
"error": "function arguments expected near '='"
},
"description": "Error happened while executing Lua script"
}
此属性可以设置加载时间,单位是秒。如果设置为0或nil,代表不检测超时,如下所示:
function main(splash)
splash.resource_timeout=0.1
assert(splash:go('https://www.taobao.com'))
return splash:png()
end
此属性可以设置图片是否加载,默认情况下是加载的。禁用该属性后,可以节省网络流量并提高网页加载速度。
function main(splash, args)
splash.images_enabled=false
assert(splash:go('https://www.jd.com'))
return {
png=splash:png()
}
end
这样返回的页面截图就不会带有任何图片,加载速度也会快很多。
此属性可以控制浏览器插件是否开启。默认情况下是不开启的。
通过设置此属性,我们可以控制页面上下或左右滚蛋。这是一个比较常用的属性,示例如下:
function main(splash, args)
assert(splash:go('https://www.taobao.com'))
splash.scroll_position = {y=400}
return {png=splash:png()}
end
这样我们就可以控制页面向下滚动400像素值
该方法是用来请求某个链接的,而且它可以模拟GET和POST请求,同时支持传入请求头、表单等数据,用法如下:
ok,reason=splash:go{url,baseurl=nil,header=nil,http_method="GET",body=nil,formdata=nil}
该方法的返回结果是结果ok和原因reason的组合,如果ok为空,代表网页加载出现了错误,此时reason变量中包含了错误的原因,否则证明加载成功,如下所示:
function main(splash, args)
local ok,reason=splash:go{"http://httpbin.org/post",http_method="POST",body="name=Germey"}
if ok then
return splash:html()
end
end
这里我们模拟一个POST请求,并传入了POST的表单数据,如果成功,则返回页面的源代码。
此方法可以控制页面的等待时间,使用方法如下:
ok,reason=splash:wait{time,cancel_on_redirect=false,cancel_on_error=true}
返回结果同样是结果ok和原因reason的组合。
如下所示:
function main(splash)
splash:go("http://www.taobao.com")
splash:wait(2)
return {
html=splash:html()
}
end
这可以实现访问淘宝并等待2秒,随后返回页面源代码的功能。
此方法可以直接调用JavaScript定义的方法,但是所调用的方法需要用中括号包围,这相当于实现了JavaScript方法到Lua脚本的转换,示例如下:
function main(splash, args)
local get_div_count=splash:jsfunc([[
function(){
var body=document.body;
var divs=body.getElementsByTagName('div');
return divs.length;
}
]])
splash:go("https://www.baidu.com")
return ("There are %s DIVs"):format(get_div_count())
end
我们声明了一个JavaScript定义的办法,然后在页面加载成功后调用了从方法计算出了页面中div节点的个数。
此方法可以执行JavaScript代码并返回最后一条JavaScript语句返回结果,使用方法如下:
result=splash:evaljs(js)
此方法可以执行JavaScript代码,它与evaljs()的功能类似,但是更偏向于执行某些功能或声明某些办法。如下所示:
function main(splash, args)
splash:go("https://www.baidu.com")
splash:runjs("foo=function(){return 'bar'}")
local result=splash:evaljs('foo()')
return result
end
这里我们用runjs()先声明了一个JavaScript定义的方法,然后通过evaljs()来调用得到的结果。
此方法可以设置每个页面访问时自动加载的对象,使用方法如下:
ok,reason=splash:autoload{source_or_url,source=nill,url=nil}
此方法只负责加载JavaScript代码或库,不执行任何操作。
function main(splash, args)
splash:autoload([[
function get_document_title(){
return document.title
}
]])
splash:go("https:www.baidu.com")
return splash:evaljs("get_document_title()")
end
这里我们调用autoload()方法声明了一个JavaScript方法,然后通过evaljs()方法来执行此JavaScript()该方法。
此方法可以通过设置定时任务和延迟时间来实现任务延时执行,并且可以在执行前通过cancel()方法重新执行定时任务。如下所示:
function main(splash, args)
local snapshots={}
local timer=splash:call_later(function()
snapshots["a"]=splash:png()
splash:wait(1.0)
snapshots["b"]=splash:png()
end,0.2)
splash:go("https://www.taobao.com")
splash:wait(3.0)
return snapshots
end
这里我们设置了一个定时任务,0.2秒的时候获取网页截图,然后等待1秒,1.2秒时再次获取网页截图,访问的页面是淘宝,最后将截图结果返回。
此方法可以模拟发送HTTP的GET请求,使用方法如下:
response=splash:http_get{url,headers=nil,follow_redirects=true}
function main(splash, args)
local treat=require("treat")
local response=splash:http_get("http://httpbin.org/get")
return {
html=treat.as_string(response.body),
url=response.url,
status=response.status
}
end
和http_get()方法类似,此方法用来模拟发送POST请求,不过多了一个参数body,使用方法如下:
response=splash:http_post{url,headers=nil,follow_redirects=true,body=nil}
function main(splash, args)
local treat=require("treat")
local json=require("json")
local response=splash:http_post{"http://httpbin.org/post",
body=json.encode({name="Germey"}),
headers={["content-type"]="application/json"}
}
return {
html=treat.as_string(response.body),
url=response.url,
status=response.status
}
end
这里我们成功模拟提交了POST请求并发生了表单数据
此方法用来设置页面的内容,示例如下:
function main(splash)
assert(splash:set_content("hello
"))
return splash:png()
end
此方法用来获取网页的源代码,它是非常简单又常用的方法。示例如下:
function main(splash, args)
splash:go("https://httpbin.org/get")
return splash:html()
end
此方法用来获取PNG格式的网页截图,示例如下:
function main(splash, args)
splash:go("https://www.taobao.com")
return splash:png()
end
此方法用来获取JPEG格式的网页截图,示例如下:
function main(splash, args)
splash:go("https://www.taobao.com")
return splash:jpeg()
end
此方法用来获取页面加载过程描述,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:har()
end
此方法可以获取当前正在访问的URL,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:url()
end
此方法可以获取当前页面的Cookies,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:get_cookies()
end
此方法可以为当前页面添加Cookie
cookies=splash:add_cookie{name,value,path=nil,expires=nil,httpOnly=nil,secure=nil}
此方法可以清楚所有的cookies
此方法可以获取当前浏览器页面的大小,即宽高。
此方法可以设置当前浏览器页面的大小,即宽高。
此方法可以设置浏览器全屏显示
此方法可以设置浏览器的User-Agent
此方法可以设置请求头
该方法可以选中符合条件的第一个节点,如果有多个节点符合条件,则只会返回一个,其参数是CSS选择器,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com/")
input=splash:select("#kw")
input:send_text("Splash")
splash:wait(3)
return splash:png()
end
我们这里首先访问了百度,然后选中了搜索框,随后调用了send_text()方法填写了文本,然后返回网页截图。
此方法可以选中所有符合条件的节点,其参数是CSS选择器。示例如下:
function main(splash, args)
local treat=require('treat')
assert(splash:go("http://quotes.toscrape.com/"))
assert(splash:wait(0.5))
local texts=splash:select_all('.quote .text')
local results={}
for index,text in ipairs(texts) do
results[index]=text.node.innerHTML
end
return treat.as_array(results)
end
这里通过CSS选择器选中了节点的正文内容,随后遍历了所有节点,将其中的文本获取下来。
此方法可以模拟鼠标点击操作,传入的参数为坐标值x和y。此外,也可以直接选中某个节点,然后调用此方法,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com/")
input=splash:select("#kw")
input:send_text('Splash')
submit=splash:select('#su')
submit:mouse_click()
splash:wait(3)
return splash:png()
end
这里我们首先选中页面的输入框,输入文本,然后选中提交按钮,调用了mouse_click()方法提交查询,然后页面等待三秒,返回截图。
此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口的名称
如果用Python实现的话,如下所示:
import requests
url='http://192.168.99.100:8050/render.html?url=https://www.baidu.com'
response=requests.get(url)
print(response.text)
这样就可以成功输出百度页面渲染后的源代码,此接口还可以指定其他参数
此接口可以获取网页截图,其参数比render.html多了几个,比如通过width和height来控制宽高,它返回的是PNG格式的图片二进制数据,示例如下:
import requests
url='http://192.168.99.100:8050/render.png?url=https://www.jd.com&wait=5&wait=1000&height=700'
response=requests.get(url)
with open('taobao.png','wb') as f:
f.write(response.content)
这样我们就成功获取了京东首页渲染完成后的页面截图
此接口和render.png不过它返回的是JPEG格式的图片二进制数据
另外,此接口比render.png多了个参数quality,用来设置图片质量。
此接口用于获取页面加载的HAR数据,
此接口包含了前面接口的所有功能,返回结果是JSON格式.
此接口才是最为强大的接口。execute接口可以实现一些交互操作
import requests
from urllib.parse import quote
lua="""
function main(splash)
return 'hello'
end
"""
url='http://192.168.99.100:8050/execute?lua_source='+quote(lua)
response=requests.get(url)
print(response.text)
这里我们用Python中的三引号将Lua脚本包括起来,然后用urllib.parse模板里的quote()方法将脚本进行URL转码,随后构造了Splash请求URL,将其作为lua_source参数传递,我们通过实例看下:
import requests
from urllib.parse import quote
lua="""
function main(splash,args)
local treat=require("treat")
local response=splash:http_get("http://httpbin.org/get")
return {
html=treat.as_string(response.body),
url=response.url,
status=response.status
}
end
"""
url='http://192.168.99.100:8050/execute?lua_source='+quote(lua)
response=requests.get(url)
print(response.text)
我们所说的Lua脚本均可以用此方法与Python进行对接。