Splash使用
Splash是一个JavaScript渲染服务,是一个带有HTTP API的轻量级浏览器,同时它对接了Python中的Twisted和QT库。 利用它,我们同样可以实现动态渲染页面的抓取。
一,安装
本安装使用docker
1.1拉去镜像
docker pull scrapinghub/splash
1.2运行
docker run -p 8050:8050 scrapinghub/splash
二,使用
(一)Splash Lua 脚本
Splash 可以通过 Lua 脚本执行一系列渲染操作,这样我们就可以用 Splash 来模拟浏览器的操作了,Splash Lua 基础语法如下:
function main(splash, args) # main()方法是入口,名称必须固定的,Splash默认调用这个方法
splash:go("http://www.baidu.com") # go()方法用于加载页面
splash:wait(0.5) # wait()方法用于等待指定的时间
local title = splash:evaljs("document.title") # evaljs()方法用于传入并执行JavaScript脚本,这里将执行结果赋值给title变量
return title # 返回title变量的值,结果是一个字符串:"百度一下,你就知道"
end
demo:
function main(splash, args)
local example_urls = {"www.baidu.com", "www.hao123.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
(二)Splash对象属性
main()方法的第一个参数是splash,这个对象非常重要,它类似于Selenium中的WebDriver对象,我们可以调用它的一些属性和方法来控制加载过程。
1.agrs
该属性可以获取加载时配置的参数,比如URL,如果为GET请求,它还可以获取GET请求参数;如果为POST请求,它可以获取表单提交的数据。Splash也支持使用第二个参数直接作为args
function main(splash, args)
local url = args.url
return {url=url}
end
等价于
function main(splash, args)
local url = splash.args.url
return {url=url}
end
2.js_enabled
这个属性是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
结果:
{
"error": 400,
"description": "Error happened while executing Lua script",
"info": {
"error": "unknown JS error: None",
"message": "[string \"function main(splash, args)\r...\"]:4: unknown JS error: None",
"source": "[string \"function main(splash, args)\r...\"]",
"js_error_message": null,
"splash_method": "evaljs",
"type": "JS_ERROR",
"line_number": 4
},
"type": "ScriptError"
}
调用了evaljs()方法执行JavaScript代码,此时运行结果就会抛出异常。
不过一般来说,不用设置此属性,默认开启即可。
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
结果:
{
"error": 400,
"description": "Error happened while executing Lua script",
"info": {
"error": "network5",
"message": "Lua error: [string \"function main(splash)\r...\"]:3: network5",
"source": "[string \"function main(splash)\r...\"]",
"line_number": 3,
"type": "LUA_ERROR"
},
"type": "ScriptError"
}
这里将超时时间设置为0.1秒。如果在0.1秒之内没有得到响应,就会抛出异常。此属性适合在网页加载速度较慢的情况下设置。如果超过了某个时间无响应,则直接抛出异常并忽略即可。
4.images_enabled
此属性可以设置图片是否加载,默认情况下是加载的。禁用该属性后,可以节省网络流量并提高网页加载速度。但是需要注意的是,禁用图片加载可能会影响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
这样返回的页面截图就不会带有任何图片,加载速度也会快很多。
5.plugins_enabled
此属性可以控制浏览器插件(如Flash插件)是否开启。默认情况下,此属性是false,表示不开启。可以使用如下代码控制其开启和关闭。
splash.plugins_enabled = true/false
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}
(三)Splash对象方法
1.go()
请求某个链接,可传入请求头、表单等数据.
ok,reason = splash:go{url, baseurl=nil, headers=nil, http_method="GET", body=nil , formdata=nil}
- url:请求的url
- baseurl:可选参数,默认为空,表示资源和加载相对路径
- headers:可选参数,默认为空,表示请求头
- http_method:可选参数,默认为GET
- body:可选参数,默认为空,POST的时候的表单数据,使用的content-type为application/json
- formdata:可选参数,默认为空,POST的时候的表单数据,使用的content-type为application/x-www-form-urlencoded
2.wait()
控制页面等待时间.
ok, reason = splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}
- time:等待的时间
- cancel_on_error:可选参数,默认false,表示发生错误加载,就停止等待
- cancel_on_redirect:可选参数,默认false,表示发生重定向就停止等待,并返回重定向结果
3.evaljs()
执行JavaScript代码后,返回最后一条JavaScript语句返回的结果
result = splash:evaljs(js)
4.runjs()
声明JavaScript定义的语句块,通过evaljs()调用
function main(splash, args)
splash:go(args.url)
splash:runjs("foo = function(){return 'hello'}")
local result = splash:evaljs("foo()")
return result
end
5.autoload()
设置每个页面访问时自动加载的对象,可以是JavaScript代码或库,但是不执行操作,执行调用evaljs()
ok,reason = splash:autoload{source_or_url}
- source_or_url:js代码或js库链接
- source:js代码
- url:js库链接
6.call_later()
设置定时任务和延时执行,如下在go访问后使用wait(3.0)等待3秒才返回所有截图,期间设置0.2秒获取截图,中间等待1秒,1.2秒的时候再次获取截图,最后3秒后返回所有截图
function main(splash, args)
local snapshots = {}
local timer = splash:call_later(function()
snapshots["a"]=splash:png()
splash:wait(1)
snapshots["b"]=splash:png()
end,0.2)
splash:go("https://www.taobao.com")
splash:wait(3.0)
return snapshots
end
7.http_get()
模拟发送http的get请求
response = splash:http_get(url,headers=nil,follow_redirects=true)
- url:请求的url
- headers:可选参数,默认为空,请求头
- follow_redirects:可选参数,表示是否启动自动重定向,默认true
8.http_post()
发送post请求,需要body参数
response = splash:http_post{url, headers =「1il, 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{args.url,
body=json.encode({name="aha"}),
headers={["content-type"]="application/json"}
}
return {
html = treat.as_string(response.body),
url = response.url,
status = response.status
}
end
9.set_content()
设置页面内容
function main(splash)
assert(splash:set_content("<html><body><hl>hello</hl></body></html>"))
return splash: png ()
end
10.html()
返回获取的页面源码信息
function main(splash, args)
splash:go("https://httpbin.org/get")
return splash: html()
end
png(),jpeg(),har(),url()
function main(splash, args)
splash:go("https:/taobao.com")
return splash: png()
end
function main(splash, args)
splash:go("https:/taobao.com")
return splash: jpeg()
end
function main(splash, args)
splash:go("https:/taobao.com")
return splash: har()
end
function main(splash, args)
splash:go("https:/taobao.com")
return splash: url()
end
11.get_cookies()
获取当前访问链接的cookies
function main(splash, args)
splash:go("https:/taobao.com")
return splash: get_cookies()
end
12.add_cookies()
添加cookies
cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}
demo
function main(splash)
splash:add_cookie{"sessionid","23746Sghgfsd ","/", domain="http://example.com"}
splash:go("http://example.com/")
return splash:html()
end
13.clear_cookies()
清除cookies
function main(splash, args)
splash:go("https:/taobao.com")
splash:clear_cookies()
return splash: get_cookies()
end
14.get_viewport_size()
获取当前浏览器的大小
function main(splash, args)
splash:go("https:/taobao.com")
return splash:get_viewport_size()
end
15.set_viewport_size()
设置当前浏览器的大小
function main(splash, args)
splash:set_viewport_size(1200,800)
assert(splash:go("https:/taobao.com"))
return splash:png()
end
16.set_viewport_full()
设置浏览器全屏显示
function main(splash, args)
splash:set_viewport_full()
assert(splash:go("https:/taobao.com"))
return splash:png()
end
17.set_user_agent()
设置浏览器的user_agent
function main(splash, args)
splash:set_user_agent('Splash')
assert(splash:go("http://httpbin.org/get"))
return splash:html()
end
18.set_custom_headers()
设置请求头信息
function main(splash, args)
splash:set_custom_headers({
["User-Agent"] = "Splash",
["Sitle"] = "Splash",
})
assert(splash:go("http://httpbin.org/get"))
return splash:html()
end
19.select()
返回选中符合条件的第一个节点,参数是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
20.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
21.mouse_click()
模拟鼠标点击事件
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
三,Splash API调用
如何才能利用Splash渲染页面呢?怎样才能和Python程序结合使用并抓取JavaScript渲染的页面呢?
其实Splash给我们提供了一些HTTP API接口,我们只需要请求这些接口并传递相应的参数即可。
1. render.html
此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称。
例如http://localhost:8050/render.html。
import requests
url = 'http://localhost:8050/render.html?url=https://www.baidu.com'
response = requests.get(url)
print(response.text)
2.render.png
此接口可以获取网页截图,其参数比render.html多了几个,比如通过width和height来控制宽高,它返回的是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('taobao.png', 'wb') as f:
f.write(response.content)
3.render.jpeg
此接口和render.png类似,不过它返回的是JPEG格式的图片二进制数据。
另外,此接口比render.png多了参数quality,它用来设置图片质量。
4.render.har
此接口用于获取页面加载的HAR数据
curl http://localhost:8050/render.har?url=https://www.jd.com&wait=5
它的返回结果非常多,是一个JSON格式的数据,其中包含页面加载过程中的HAR数据。
5.render.json
此接口包含了前面接口的所有功能,返回结果是JSON格式。
curl http://localhost:8050/render.json?url=https://httpbin.org
可以看到,这里以JSON形式返回了相应的请求数据。
我们可以通过传入不同参数控制其返回结果。比如,传入html=1,返回结果即会增加源代码数据;传入png=1,返回结果即会增加页面PNG截图数据;传入har=1,则会获得页面HAR数据。
import requests
url = 'http://localhost:8050/render.json?url=https://httpbin.org&html=1&har=1'
response = requests.get(url)
print(response.text)
这样返回的JSON结果会包含网页源代码和HAR数据
可参考:https://splash.readthedocs.io/en/stable/api.html#render-json
5.⭐execute
此接口才是最为强大的接口。前面说了很多Splash Lua脚本的操作,用此接口便可实现与Lua脚本的对接。
前面的render.html和render.png等接口对于一般的JavaScript渲染页面是足够了,但是如果要实现一些交互操作的话,它们还是无能为力,这里就需要使用execute接口了。
使用步骤:
①lua脚本
function main(splash)
return 'hello'
end
②改为url编码、
使用网站:在线编码转换 (oschina.net)
function%20main(splash)%0A%20%20%20%20return%20'hello'%0Aend
③运行语句:
curl http://localhost:8050/execute?lua_source=function+main%28splash%29%0D%0A++return+%27hello%27%0D%0Aend
④构建python
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)
四,Splash负载均衡配置
假设有多台服务器在8050端口配置了 splash 服务,都是通过 docker 的 splash 开启,都可以使用服务。
1.配置负载均衡
接下来,可以选用任意一台带有公网 IP 的主机来配置负载均 首先,在这台主机上装好 Nginx,
然后修改 Nginx 的配置文件 nginx.conf,添加如下内容:
http {
upstream splash {
# least_conn 代表最少链接负载均衡,它适合处理请求处理时间长短不一造成服务器过载的情况
# ip_hash 服务器根据请求客户端的 IP 地址进行散列计算,确保使用同一个服务器响应请求,这种
# 策略适合有状态的服务,比如用户登录后访问某个页面的情形
# server 119.45.0.45:8050; 可以指定权重 server 119.45.0.45:8050 weight=4;
least_conn;
server 119.45.0.45:8050;
server 167.179.88.120:8050;
}
server {
listen 8050;
location / {
proxy_pass http://splash;
}
}
}
2.配置认证
现在 Splash 是可以公开访问的,如果不想让其公开访问,还可以配置认证,这仍然借助于 Nginx
可以在 server 的 location 字段中添加 auth_basic 和 auth_basic_user_file 字段,具体配置如下:
http {
upstream splash {
least_conn;
server 119.45.0.45:8050;
server 167.179.88.120:8050;
}
server {
listen 8050;
location / {
proxy_pass http://splash;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
}
}
}
这里使用的用户名和密码配置放置在 /etc/nginx/conf.d 目录下,我们需要使用 htpasswd 命令创建
例如,创建一个用户名为 admin 的文件,相关命令如下:
htpasswd -c .htpasswd admin
接下来就会提示我们输入密码,输入两次之后,就会生成密码文件,其内容如下:
cat .htpasswd
admin:SZBxQrorCqwbc
配置完成后,重启 Nginx 服务:
sudo nginx -s reload
3.测试
import requests
from urllib.parse import quote
import re
lua = '''
function main(splash, args)
local treat = require("treat")
local response = splash:http_get("http://httpbin.org/get")
return treat.as_string(response.body)
end
'''
url = 'http://127.0.0.1:8050/execute?lua_source=' + quote(lua)
response = requests.get(url)
// 如果配置了密码 response = requests.get(url, auth=('admin', 'admin'))
ip = re.search('(\d+\.\d+\.\d+\.\d+)', response.text).group(1)
print(ip)
标签:function,end,url,args,splash,main,Splash
From: https://www.cnblogs.com/xueyitian/p/18003722