Splash

安装,连接及配置

Docker的安装

docker

Splash

GitHub:https://github.com/scrapy-plugins/scrapy-splash

PyPI:https://pypi.python.org/pypi/scrapy-splash

使用说明:https://github.com/scrapy-plugins/scrapy-splash#configuration

Splash官方文档:http://splash.readthedocs.io

docker安装Splash:docker run -p 8050:8050 scrapinghub/splashSplash在8050端口上运行。这时我们打开http://localhost:8050,即可看到Splash的主页

docker run -d -p 8050:8050 scrapinghub/splash-d参数,它代表将Docker容器以守护态运行,这样在中断远程服务器连接后,不会终止Splash服务的运行

Scrapy-Splash的安装

安装:pip3 install scrapy-splash

Splash Lua脚本

  • 入口及返回值:
function main(splash, args)  -- 方法名称叫作main()。这个名称必须是固定的,Splash会默认调用这个方法
  assert(splash:go(args.url))  --go()方法去加载页面
  assert(splash:wait(0.5))  --wait()方法等待了一定时间,参数为等待的秒数,它会转而去处理其他任务,然后在指定的时间过后再回来继续处理
  local title = splash:evaljs("document.title")  --evaljs()方法传入JavaScript脚本,document.title的执行结果就是返回网页标题,执行完毕后将其赋值给一个title变量
  return {  -- 返回值既可以是字典形式,也可以是字符串形式,最后都会转化为Splash HTTP Response
    html = splash:html(),  --返回了页面的源码、截图和HAR信息
    png = splash:png(),
    har = splash:har(),
    title=title  --返回title变量
  }
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  --迭代函数 ipairs
    local ok, reason = splash:go("http://" .. url)  -- 字符串拼接使用的是..操作符
    if ok then  -- 做了加载时的异常检测。go()方法会返回加载页面的结果状态,如果页面出现4xx或5xx状态码,ok变量就为空,就不会返回加载后的图片
      splash:wait(2)
      results[url] = splash:png()
    end
  end
  return results  -- 返回是3个站点的截图
end

Splash对象属性

main()方法的第一个参数是splash,这个对象非常重要,它类似于Selenium中的WebDriver对象,我们可以调用它的一些属性和方法来控制加载过程

  • args

该属性可以获取加载时配置的参数,比如URL(splash.args.url),如果为GET请求,它还可以获取GET请求参数;如果为POST请求,它可以获取表单提交的数据。Splash也支持使用第二个参数直接作为args(args.url)

  • js_enabled

JavaScript执行开关(splash:evaljs()),可以将其配置为true或false来控制是否执行JavaScript代码,默认为true

  • resource_timeout

可以设置加载的超时时间,单位是秒。如果设置为0或nil(类似Python中的None),代表不检测超时

  • images_enabled

可以设置图片是否加载,默认情况下是加载的。禁用该属性(splash.images_enabled = false)后,可以节省网络流量并提高网页加载速度。但是需要注意的是,禁用图片加载可能会影响JavaScript渲染。

  • plugins_enabled

控制浏览器插件(如Flash插件)是否开启(splash.plugins_enabled = true/false)。默认情况下,此属性是false,表示不开启

  • scroll_position

通过设置此属性,我们可以控制页面上下或左右滚动(splash.scroll_position = {x=100, y=200})x代表右滚动,y代表下滚动

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变量中包含了错误的原因,否则证明页面加载成功。
  • wait()

以控制页面的等待时间,使用方法如下:ok, reason = splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}

+ time:等待的秒数。
+ cancel_on_redirect:可选参数,默认为false,表示如果发生了重定向就停止等待,并返回重定向结果。
+ cancel_on_error:可选参数,默认为false,表示如果发生了加载错误,就停止等待

结果同样是结果ok和原因reason的组合
  • jsfunc()

可以直接调用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
  • evaljs()

可以执行JavaScript代码并返回最后一条JavaScript语句的返回结果.result = splash:evaljs(js)

  • runjs()

可以执行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
  • autoload()

可以设置每个页面访问时自动加载的对象,只负责加载JavaScript代码或库,不执行任何操作。使用方法如下:
ok, reason = splash:autoload{source_or_url, source=nil, url=nil}

+ source_or_url:JavaScript代码或者JavaScript库链接。
+ source:JavaScript代码。
+ url:JavaScript库链接

例如:

splash:autoload([[  --声明了一个JavaScript方法
    function get_document_title(){
      return document.title;
    }
  ]])


assert(splash:autoload("https://code.jquery.com/jquery-2.1.3.min.js"))  --加载jQuery库
  • 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)  --截图任务,定时0.2
  splash:go("https://www.taobao.com")
  splash:wait(3.0)
  return snapshots
end
  • http_get()

此方法可以模拟发送HTTP的GET请求,返回response对象
‘response = splash:http_get{url, headers=nil, follow_redirects=true}’

+ url:请求URL。
+ headers:可选参数,默认为空,请求头。
+ follow_redirects:可选参数,表示是否启动自动重定向,默认为true。
  • http_post()

和http_get()方法类似,此方法用来模拟发送POST请求,不过多了一个参数body,可选参数,即表单数据,默认为空。

例如:

local json = require("json")
  local response = splash:http_post{"http://httpbin.org/post",     
      body=json.encode({name="Germey"}),
      headers={["content-type"]="application/json"}
    }
  • set_content()

用来设置页面的内容

例如:assert(splash:set_content("<html><body><h1>hello</h1></body></html>"))

  • html()

用来获取网页的源代码return splash:html()

  • png()

此方法用来获取PNG格式的网页截图

  • jpeg()

此方法用来获取JPEG格式的网页截图

  • har()

此方法用来获取页面加载过程描述
HAR(HTTP Archive),是一个用来储存HTTP请求/响应信息的通用文件格式,基于JSON。

  • url()

此方法可以获取当前正在访问的URL

  • get_cookies()

此方法可以获取当前页面的Cookies

  • add_cookie()

可以为当前页面添加Cookie,用法如下:cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}

  • clear_cookies()

此方法可以清除所有的Cookies

  • get_viewport_size()

此方法可以获取当前浏览器页面的大小,即宽高

  • et_viewport_size()

此方法可以设置当前浏览器页面的大小,即宽高splash:set_viewport_size(width, height)

  • set_viewport_full()

此方法可以设置浏览器全屏显示

  • set_user_agent()

此方法可以设置浏览器的User-Agent

  • set_custom_headers()

此方法可以设置请求头

 splash:set_custom_headers({
     ["User-Agent"] = "Splash",
     ["Site"] = "Splash",
  })
  • select()

该方法可以选中符合条件的第一个节点,如果有多个节点符合条件,则只会返回一个,其参数是CSS选择器。

  • select_all()

此方法可以选中所有符合条件的节点,其参数是CSS选择器

  • mouse_click()

此方法可以模拟鼠标点击操作,传入的参数为坐标值x和y。此外,也可以直接选中某个节点,然后调用此方法

  • send_text()方法填写了文本

官方文档https://splash.readthedocs.io/en/stable/scripting-ref.html,此页面介绍了Splash对象的所有API操作。

针对页面元素的API操作,链接为https://splash.readthedocs.io/en/stable/scripting-element-object.html

Splash API调用

  • render.html

此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称,例如接口页面http://localhost:8050/render.html,接口可指定参数

url:url = 'http://localhost:8050/render.html?url=https://www.baidu.com'
wait指定等待秒数:url = 'http://localhost:8050/render.html?url=https://www.taobao.com&wait=5'

此接口还支持代理设置、图片加载设置、Headers设置、请求方法设置,具体的用法可以参见官方文档https://splash.readthedocs.io/en/stable/api.html#render-html

  • 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)

详细的参数设置可以参考官网文档https://splash.readthedocs.io/en/stable/api.html#render-png

  • render.jpeg

此接口和render.png类似,不过它返回的是JPEG格式的图片二进制数据。另外,此接口比render.png多了参数quality,它用来设置图片质量

  • render.har

此接口用于获取页面加载的HAR数据,是一个JSON格式的数据,其中包含页面加载过程中的HAR数据

  • render.json

此接口包含了前面接口的所有功能,返回结果是JSON格式

以通过传入不同参数控制其返回结果。比如,传入html=1,返回结果即会增加源代码数据;传入png=1,返回结果即会增加页面PNG截图数据;传入har=1,则会获得页面HAR数据。

更多参数设置,具体可以参考官方文档:https://splash.readthedocs.io/en/stable/api.html#render-json

  • execute

用此接口便可实现与Lua脚本的对接。lua_source参数传递了转码后的Lua脚本,通过execute接口获取了最终脚本的执行结果

python实现:

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)

Splash负载均衡配置

    1. 配置Splash服务

要有多个Splash服务。假如这里在4台远程主机的8050端口上都开启了Splash服务,它们的服务地址分别为41.159.27.223:8050、41.159.27.221:8050、41.159.27.9:8050和41.159.117.119:8050,这4个服务完全一致,都是通过Docker的Splash镜像开启的。访问其中任何一个服务时,都可以使用Splash服务

  • 2.配置负载均衡

选用任意一台带有公网IP的主机来配置负载均衡,配置完成后重启一下Nginx服务sudo nginx -s reload。首先,在这台主机上装好Nginx,然后修改Nginx的配置文件nginx.conf,添加如下内容:

http {
    upstream splash {
        least_conn;
        server 41.159.27.223:8050;
        server 41.159.27.221:8050;
        server 41.159.27.9:8050;
        server 41.159.117.119:8050;
    }
    server {
        listen 8050;
        location / {
            proxy_pass http://splash;
        }
    }
}

upstream字段定义了一个名字叫作splash的服务集群配置

least_conn代表最少链接负载均衡,它适合处理请求处理时间长短不一造成服务器过载的情况

upstream splash {
    server 41.159.27.223:8050;
    server 41.159.27.221:8050;
    server 41.159.27.9:8050;
    server 41.159.117.119:8050;
}

默认以轮询策略实现负载均衡,每个服务器的压力相同。此策略适合服务器配置相当、无状态且短平快的服务使用。

upstream splash {
    server 41.159.27.223:8050 weight=4;
    server 41.159.27.221:8050 weight=2;
    server 41.159.27.9:8050 weight=2;
    server 41.159.117.119:8050 weight=1;
}

weight参数指定各个服务的权重,权重越高,分配到处理的请求越多。假如不同的服务器配置差别比较大的话,可以使用此种配置。

upstream splash {
    ip_hash;
    server 41.159.27.223:8050;
    server 41.159.27.221:8050;
    server 41.159.27.9:8050;
    server 41.159.117.119:8050;
}

IP散列负载均衡,服务器根据请求客户端的IP地址进行散列计算,确保使用同一个服务器响应请求,这种策略适合有状态的服务,比如用户登录后访问某个页面的情形。对于Splash来说,不需要应用此设置。

  • 3.配置认证

不想让其公开访问,还可以配置认证,这仍然借助于Nginx。可以在server的location字段中添加auth_basic和auth_basic_user_file字段,具体配置如下:

http {
    upstream splash {
        least_conn;
        server 41.159.27.223:8050;
        server 41.159.27.221:8050;
        server 41.159.27.9:8050;
        server 41.159.117.119: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接下来就会提示我们输入密码,输入两次之后,就会生成密码文件.配置完成后,重启一下Nginx服务:sudo nginx -s reload

    1. 测试

利用http://httpbin.org/get测试即可,实现代码如下:

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://splash:8050/execute?lua_source=' + quote(lua)  # splash字符串自行替换成自己的Nginx服务器IP
response = requests.get(url, auth=('admin', 'admin'))
ip = re.search('(\d+\.\d+\.\d+\.\d+)', response.text).group(1)
print(ip)
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐