Splash是一个JavaScript渲染服务 是一个带有HTTP API的轻量级浏览器 同时对接了python的Twisted 和QT库html
利用它能够实现对动态渲染页面的抓取node
功能介绍python
1.异步方式处理多个网页渲染过程 2.获取渲染后的页面源代码或截图 3.经过关闭图片渲染或使用Adblock规则加快页面渲染速度 4.可执行特定js脚本 5.可经过Lua脚原本控制页面渲染过程 6.获取渲染的详细过程并经过HAR(HTTP Archive)格式呈现
安装准备
1.Docker的安装 (后面讲到时会详细讲 这里先安装)jquery
windows下安装:
win10 64位 推荐 Docker for windwos 官网下载最新安装包:web
https://docs.docker.com/docker-for-windows/install/
不是 64位的 下载 Docker Toolbox :docker
https://docs.docker.com/toolbox/toolbox_install_windows/
下载后双击安装 安装完成后 进入命令行 输入docker 运行没有报错证实安装成功了json
2.安装splash 命令行执行 windows
docker run -p 8050:8050 scrapinghub/splash
显示以下省略部分表示服务启动了
[-] Site starting on 8050
[-] Starting factory <twisted.web.server.Site object at 0x7fb62b1957f0>api
打开浏览器 访问localhost:8050 显示web页面浏览器
尝试修改输入框为 https://www.baidu.com 点击 Render me
返回结果呈现了 渲染截图 HAR加载统计数据 网页源代码
经过HAR结果能够看到 Splash 执行了整个页面的渲染过程 包括CSS JS 加载等 和咱们在浏览器中获得的结果一致
过程控制
function main(splash, args) assert(splash:go(args.url))#加载页面 assert(splash:wait(0.5))#延时等待 return { html = splash:html(),#返回页面源码 png = splash:png(),#返回截图 har = splash:har(),#返回HAR信息 } end
2.1 Splash Lua 脚本
Splash 能够经过Lua脚本执行一系列渲染操做
2.1.1 入口及返回值
示例:
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()方法传入js脚本 执行完毕赋值给title变量 随后返回
注意:方法名 main() 是固定的 必须用main splash默认会调用该方法
返回值既能够是字典也能够是字符串 最后都会转化为Splash HTTP Response
示例:
function main(splash) return {hello="world"} end
返回字典
function main(splash) return 'hello' end
返回字符串
2.1.2 异步处理
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
脚本中调用wait方法 相似python中的sleep 单位秒
当splash执行到此方法会转而处理其余任务,而后在指定时间再回来继续处理
字符串拼接和python不一样 用的是 .. 操做符
更多Lua脚本语法:http://www.runoob.com/lua/lua-basic-syntax.html
2.2 splash 对象属性
main() 方法中第一个参数 splash 这个对象很是重要 相似selenium中webdriver对象
能够经过调用splash 的属性和方法 控制加载过程
属性
2.2.1 args 获取加载时配置参数 例如URL 若是是get请求 能够获取get请求参数 若是是post请求 能够获取表单提交数据
splash也支持第二个参数直接做为args
示例:
function main(splash,args) local url = args.url end
等价于
function main(splash) local url = spalsh.args.url end
2.2.2 js_enabled 这个属性是splash的js执行开关 能够配置成true或false 控制是否执行js代码 默认为true
例如: 禁止js执行
function main(splash,args) splash:go("http://www.baidu.com") splash.js_enabled = false local title = splash:evaljs("document.title") return {title=title} end
结果抛出异常
2.2.3 resource_timeout 设置加载超时 单位秒 若是设置为0 或者 nil(相似python中None) 表明不检测超时
示例:
function main(splash) splash.resource_timeout = 0.1 assert(splash:go("https://www.taobao.com")) return splash:png() end
此属性适合网页加载速度比较慢的状况设置 若是超时无响应抛出异常并忽略
2.2.4 images_enabled 设置图片是否加载 默认加载
优势 禁用该属性后 能够节省网络流量提升网页加载速度
缺点 可能会影响js渲染 禁用图片后外层DOM节点高度会受影响 进而影响DOM节点位置
若是js对图片节点有操做的话,执行就会受到影响
注意 splash 使用了缓存 若是开始加载了图片 而后禁用图片加载 再从新加载页面 图片还会显示 重启splash服务便可
禁用图片加载示例:
function main(splash,args) splash.images_enabled = false assert(splash:go("https://www.jd.com")) return {png=splash:png()} end
2.2.5 plugins_enabled 控制浏览器插件(例如Flash)是否开启 默认false 表示不开启
经过 splash.plugins_enabled = true/false 控制开启或关闭
2.2.6 scroll_position 设置此属性能够控制页面上下或者左右滚动 比较经常使用的属性
示例:
function main(splash,args) assert(splash:go("https://www.taobao.com")) splash.scroll_position = {y=400} return {png=splash:png()} end
若是让页面左右滚动 传入x参数 以下:
splash.scroll_position = {x=100,y=200}
2.3 splash 对象的方法
go() 请求某个连接 能够模拟GET POST 请求 同时支持传入请求头 表单等数据 用法以下:
ok,reason = splash:go{url,baseurl=nil,headers=nil,http_method="GET",body=nil,formdata=nil}
参数说明
url 请求url地址 baseurl 可选 默认空 资源加载相对路径 headers 可选 默认空 请求头 http_method 可选 默认GET 支持POST body 可选 默认空 发送post请求时表单数据 使用的Content-type application/json formdata 可选 默认空 POST请求时表单数据 使用的Content-type 为application/x-www-form-urlencoded
该方法返回的结果是ok 和 reason 的组合 若是ok为空 表明网页加载出现错误 此时reason变量中包含错误信息 不然表示页面加载成功
示例:模拟POST请求 传入表单数据 若是成功返回页面源代码
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
wait() 控制页面等待时间 用法以下:
ok,reason = splash:wait{time,cancle_on_redirect=false,cancle_on_error=true}
参数说明
time 等待秒数 cancle_on_redirect 可选 默认false 表示若是发生重定向就中止等待 并返回重定向结果 cancle_on_error 可选 默认false 表示若是发生了加载错误就中止等待
示例:
function main(splash) splash:go("https://www.taobao.com") splash:wait(2) return {html=splash:html()} end
jsfunc() 此方法能够直接调用js定义的方法 调用的方法须要用双中括号包围 至关于实现了js方法到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
更多Lua脚本的更多转换细节 官方文档:
https://splash.readthedocs.io/en/stable/scripting-ref.html#splash-jsfunc
evaljs() 能够执行js代码 并返回最后一条js语句结果 用法以下
results = splash.evaljs(js)
例如:
local title = splash.evaljs("document.title")
runjs() 执行js代码 与 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
autoload() 设置每一个页面访问时自动加载的对象 用法以下
ok,reason = splash:autoload{source_or_url,source=nil,url=nil}
参数说明
source_or_url js代码或者js库连接 source js代码 url js库连接
此方法只负责加载js代码或库 不执行任何操做 执行操做调用 evaljs() 或 runjs()
示例:
function main(splash,args) splash:autoload([[ function get_document_title(){ return document.title } ]]) splash:go("http://www.baidu.com") return splash:evaljs("get_document_title()") end
另外也能够加载某些方法库 例如JQuery
示例:
function main(splash,args) assert(splash:autoload("http://code.jquery.com/jquery-2.1.3.min.js")) assert(splash:go("https://www.taobao.com")) local version = splash:evaljs(" $.fn.jquery") return 'JQuery version: ' .. version end
call_later() 设置定时任务和延迟时间 来实现任务延时执行 而且在执行前经过 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
第一次截图时网页尚未加载出来,截图为空,第二次网页便加载成功了。
http_get() 此方法能够模拟发送HTTP的GET请求,使用方法以下:
response = splash:http_get{url, headers=nil, follow_redirects=true}
参数说明以下
url:请求URL headers:可选参数,默认为空,请求头。 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_post() 此方法用来模拟发送POST请求,不过多了一个参数body,使用方法以下:
response = splash:http_post{url, headers=nil, follow_redirects=true, body=nil}
参数说明以下
url:请求URL headers:可选参数,默认为空,请求头。 follow_redirects:可选参数,表示是否启动自动重定向,默认为true。 body:可选参数,即表单数据,默认为空。
示例以下:
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请求并发送了表单数据
set_content() 此方法用来设置页面的内容.
示例以下:
function main(splash) assert(splash:set_content("<html><body><h1>hello</h1></body></html>")) return splash:png() end
返回了咱们设置的内容
html() 此方法用来获取网页的源代码
示例以下:
function main(splash, args) splash:go("https://httpbin.org/get") return splash:html() end
png() 此方法用来获取PNG格式的网页截图
示例以下
function main(splash, args) splash:go("https://www.taobao.com") return splash:png() end
jpeg() 此方法用来获取JPEG格式的网页截图
示例以下:
function main(splash, args) splash:go("https://www.taobao.com") return splash:jpeg() end
har() 此方法用来获取页面加载过程描述 示例以下:
function main(splash, args) splash:go("https://www.baidu.com") return splash:har() end
url() 此方法能够获取当前正在访问的URL,示例以下:
function main(splash, args) splash:go("https://www.baidu.com") return splash:url() end
get_cookies() 此方法能够获取当前页面的Cookies,示例以下:
function main(splash, args) splash:go("https://www.baidu.com") return splash:get_cookies() end
add_cookie() 此方法能够为当前页面添加Cookie,用法以下
cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}
该方法的各个参数表明Cookie的各个属性
示例以下:
function main(splash) splash:add_cookie{"sessionid", "237465ghgfsd", "/", domain="http://example.com"} splash:go("http://example.com/") return splash:html() end
clear_cookies() 此方法能够清除全部的Cookies,示例以下
function main(splash) splash:go("https://www.baidu.com/") splash:clear_cookies() return splash:get_cookies() end
清除了全部的Cookies,而后调用 get_cookies()将结果返回
get_viewport_size() 此方法能够获取当前浏览器页面的大小,即宽高
function main(splash) splash:go("https://www.baidu.com/") return splash:get_viewport_size() end
set_viewport_size() 此方法能够设置当前浏览器页面的大小,即宽高 用法以下:
splash:set_viewport_size(width, height) function main(splash) splash:set_viewport_size(400, 800) assert(splash:go("https://h5.m.taobao.com/")) return splash:png() end
set_viewport_full() 此方法能够设置浏览器全屏显示,示例以下:
function main(splash) splash:set_viewport_full() assert(splash:go("https://h5.m.taobao.com/")) return splash:png() end
set_user_agent() 此方法能够设置浏览器的User-Agent,示例以下:
function main(splash) splash:set_user_agent('Splash') splash:go("http://httpbin.org/get") return splash:html() end
将浏览器的User-Agent设置为Splash
set_custom_headers() 此方法能够设置请求头,示例以下:
function main(splash) splash:set_custom_headers({ ["User-Agent"] = "Splash", ["Site"] = "Splash", }) splash:go("http://httpbin.org/get") return splash:html() end
设置了请求头中的User-Agent和Site属性
select() 该方法能够选中符合条件的第一个节点,
若是有多个节点符合条件,则只会返回一个,其参数是CSS选择器。示例以下:
function main(splash) splash:go("https://www.baidu.com/") input = splash:select("#kw") input:send_text('Splash') splash:wait(3) return splash:png() end
首先访问了百度,而后选中了搜索框,随后调用了 send_text()方法填写了文本,而后返回网页截图
select_all() 此方法能够选中全部符合条件的节点,其参数是CSS选择器。示例以下:
function main(splash) 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选择器选中了节点的正文内容,随后遍历了全部节点,将其中的文本获取下来
mouse_click() 此方法能够模拟鼠标点击操做,传入的参数为坐标值x和y。
也能够直接选中某个节点,而后调用此方法,示例以下:
function main(splash) 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()方法提交查询,而后页面等待三秒,返回截图
Splash的更多API操做 官方文档
https://splash.readthedocs.io/en/stable/scripting-ref.html
针对页面元素的API操做
https://splash.readthedocs.io/en/stable/scripting-element-object.html
2.4 Splash API调用
前面说明了Splash Lua脚本的用法,但这些脚本是在Splash页面中测试运行的,如何才能利用Splash渲染页面,
怎么才能和Python程序结合使用并抓取JavaScript渲染的页面
Splash提供了一些HTTP API接口,只须要请求这些接口并传递相应的参数便可
2.4.1 render.html
此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称,
例如http://localhost:8050/render.html
用Python实现的话,代码以下
import requests url = 'http://localhost:8050/render.html?url=https://www.baidu.com' response = requests.get(url) print(response.text)
另外,此接口还能够指定其余参数,好比经过wait指定等待秒数。
若是要确保页面彻底加载出来,能够增长等待时间,例如:
import requests url = 'http://localhost:8050/render.html?url=https://www.taobao.com&wait=5' response = requests.get(url) print(response.text)
此接口还支持代理设置、图片加载设置、Headers设置、请求方法设置,
具体的用法能够参见官方文档https://splash.readthedocs.io/en/stable/api.html#render-html
2.4.2 render.png
此接口能够获取网页截图,其参数比render.html多了几个,
好比经过width和height来控制宽高,它返回的是PNG格式的图片二进制数据。示例以下
用Python实现,能够将返回的二进制数据保存为PNG格式的图片
import requests url = 'http://localhost:8050/render.png?url=https://www.jd.com&wait=5&width=1000&height=700' response = requests.get(url) with open('jd.png', 'wb') as f: f.write(response.content)
详细的参数设置能够参考官网文档https://splash.readthedocs.io/en/stable/api.html#render-png
2.4.3 render.jpeg
此接口和render.png相似,不过它返回的是JPEG格式的图片二进制数据。
另外,此接口比render.png多了参数quality,它用来设置图片质量。
2.4.4 render.har
此接口用于获取页面加载的HAR数据,示例以下:
http://localhost:8050/render.har?url=https://www.jd.com&wait=5
是一个JSON格式的数据,其中包含页面加载过程当中的HAR数据
2.4.5 render.json
此接口包含了前面接口的全部功能,返回结果是JSON格式,示例以下
http://localhost:8050/render.json?url=https://httpbin.org
此外还有更多参数设置,具体能够参考官方文档:https://splash.readthedocs.io/en/stable/api.html#render-json
2.4.6 execute
此接口可实现与Lua脚本的对接
示例1
import requests from urllib.parse import quote lua = ''' function main(splash) return 'hello' end ''' url = 'http://localhost:8050/execute?lua_source=' + quote(lua) response = requests.get(url) print(response.text)
经过lua_source参数传递了转码后的Lua脚本,经过execute接口获取了最终脚本的执行结果
示例2
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://localhost:8050/execute?lua_source=' + quote(lua) response = requests.get(url) print(response.text)
用urllib.parse模块里的 quote()方法将脚本进行URL转码,
随后构造了Splash请求URL,将其做为lua_source参数传递,这样运行结果就会显示Lua脚本执行后的结果
返回结果是JSON形式,咱们成功获取了请求的URL、状态码和网页源代码
以前所说的Lua脚本都可以用此方式与Python进行对接,全部网页的动态渲染、模拟点击、表单提交、页面滑动、延时等待后的一些结果都可以自由控制。