Splash是一个JavaScript渲染服务,是一个带有HTTP API的轻量级浏览器,同时它对接了Python中的Twisted和QT库。
利用它,我们同样可以实现动态渲染页面的抓取。

安装

ubuntu 安装docker 命令

curl -sSL https://get.daocloud.io/docker | sh

  或者

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

启动docker

sudo docker systemctl start docker

安装Splash 拉取docker镜像

sudo docker pull scrapinghub/splash

 

拉取成功后启动服务器

启动命令为:

docker run -p 8050:8050 -p 5023:5023 scrapinghub/splash

最后再浏览器中打开

功能介绍

  1. 异步方式处理多个网页渲染过程

  2. 获取渲染后页面的源代码或截图

  3. 通过关闭图片渲染或者使用Adblock规则来加快页面渲染速度

  4. 可执行特定的JavaScript脚本

  5. 可通过Lua脚本来控制页面渲染过程

  6. 获取渲染的详细过程并通过HAR(HTTP Archive)格式呈现

功能实例

go 用来请求某个链接

可以模拟get和post请求,同时传入请求头、表单等数据,赋值传入变量时用{}
go方法有2个返回值,ok和reason,
ok为空代表网页加载出现错误,
reason变量中包含错误原因

  1. url:请求的URL
  2. baseurl:可选参数,默认为空,表示自愿加载的相对路径
  3. headers:可选参数,默认为空,请求头
  4. http_method:可选参数,默认为get,可以支持post
  5. body:可选参数,默认为空,发post请求时的表单数据,传入的数据内容类型为json
  6. formdata:可选参数,默认为空,发post请求时的表单数据,传入的数据内容类型为x-www-form-urlencoded
function main(splash, args)
  local ok, reason = splash:go{
            url="http://httpbin.org/post", 
            http_method="POST", 
            body="name=dmr"
                }
  splash:wait(2)
  if ok then
    return splash:html()
  end
end

异步处理 机制

异步处理
ipairs,为集合元素进行编号(编号从1开始),类似于python的enumerate
lua脚本语言中字符串拼接用 ..
splash:wait()类似于python中的time.sleep()
当Splash执行wait方法时,它会转而去处理其他任务,等到指定时间结束后再回来进行继续处理

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

JavaScript等的操作方法

jsfunc方法

可以直接调用JavaScript定义的方法,所调用的方法要用双括号包围
如下示例,通过构造JavaScript方法来获取访问页面的title和div数量并返回

function main(splash, args)
  local get_div_count = splash:jsfunc([[
    function(){
    var title = document.title;
    var body = document.body;
    var divs = body.getElementsByTagName('div');
    var div_count = divs.length;
    return {div_count, title};
      }
    ]])
  ok, reason = splash:go("https://www.mi.com")
  result = get_div_count()
  return ("This page'title is %s, there are %s divs"):format(result.title, result.div_count)
end

evaljs方法

可以执行JavaScript代码并返回最后一条JavaScript的返回结果

如下示例,只返回最后一条JavaScript语句的执行结果

function main(splash, args)
  splash:go("https://www.mi.com")
  result = splash:evaljs("document.title;document.body.getElementsByTagName('div').length;")
  return result
end

runjs方法

可以执行JavaScript代码,与evaljs类似,但是更偏向于执行某些动作或声明某些方法

如下示例,用runjs方法构造了一个JavaScript定义的方法,然后用evaljs执行此方法获取返回结果

function main(splash, args)
  splash:go("https://www.mi.com")
  splash:runjs("fofo = function(){return 'dmr'}")
  result = splash:evaljs('fofo()')
  return result
end

autoload方法

可以设置每个页面访问时自动加载的对象,在Lua语言中nil相当于python的None
ok, reason = splash:autoload{source_or_url, source=nil, url=nil}

  1. source_or_url:JavaScript代码或JavaScript库链接
  2. source:JavaScript代码
  3. url:JavaScript库链接

示例1,通过构造一个get_path_title对象方法,用evaljs调用执行获取返回结果,在这里与runjs类似,不过构造方法需要用[]中括号

function main(splash, args)
  splash:autoload([[
    function get_path_title(){
    return document.title;
  }
    ]])
  splash:go("https://www.mi.com")
  result = splash:evaljs('get_path_title()')
  return result
end

发起get 和post请求

http_get方法,模拟发送http的get请求

response = splash:http_get{url, headers=nil, follow_redirects=true}

  1. url:请求URL
  2. headers:可选参数,默认为空,请求头
  3. follwo_redirects:可选参数,表示是否启动自动重定向,默认为true
function main(splash, args)
  local t = require("treat")
  local response = splash:http_get("https://www.taobao.com")
  if response.status == 200 then
    return {
      b_html = response.body,
      html = t.as_string(response.body),
      url = response.url,
      status = response.status,
    }
  end  
end

http_post方法,模拟发送http的post请求,与http_get方法类似,不过多个body表单参数

http_post方法,模拟发送http的post请求,与http_get方法类似,不过多个body表单参数
response = splash:http_get{url, headers=nil, follow_redirects=true, body=nil}

  1. url:请求URL
  2. headers:可选参数,默认为空,请求头
  3. follwo_redirects:可选参数,表示是否启动自动重定向,默认为true
  4. body:可选参数,默认为空,表单数据

如下示例,将表单数据提交到了json中

function main(splash, args)
  local t = require("treat")
  local json = require("json")
  local response = splash:http_post{
    args.url, 
    body=json.encode({name="dmr"}),
    headers={["content-type"]="application/json"}
  }
  if response.status == 200 then
    return {
      'response',
      b_html = response.body,
      html = t.as_string(response.body),
      url = response.url,
      status = response.status, 
    }
  end  
end

Cookies的获取添加与删除

获取

function main(splash, args)
  assert(splash:go("https://www.baidu.com"))
  return {cookies = splash:get_cookies()}
end

添加

add_cookie方法,为当前页面添加cookie
splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil,secure=nil,}

function main(splash, args)
  splash:add_cookie{'name', 'dmr'}
  assert(splash:go("https://www.baidu.com"))
  return {cookies = splash:get_cookies()}
end

删除

clear_cookies方法,清楚所有的cookies

function main(splash, args)
  splash:add_cookie{'name', 'dmr'}
  assert(splash:go("https://www.baidu.com"))
  splash:clear_cookies()
  return {cookies = splash:get_cookies()}
end

select方法 和 select_all方法

select方法,查找符合条件的第一个节点,用的是CSS选择器

function main(splash, args)
  assert(splash:go("https://www.taobao.com"))
  input = splash:select("#q")
  input:send_text("数码")
  splash:wait(2)
  return splash:jpeg()
end

select_all方法,查找符合条件的所有节点,用的是CSS选择器

function main(splash, args)
  local treat = require("treat")
  assert(splash:go("https://movie.douban.com/top250"))
    assert(splash:wait(1))
  local items = splash:select_all(".inq")
  local sum = {}
  for index, item in ipairs(items) do
    sum[index] = item.node.innerHTML
  end
  return {
    obj1 = sum,
    obj2 = treat.as_array(sum)
  }
end

设置定时任务进行延时执行

call_later方法,设置定时任务进行延时执行,并且可以在执行前通过cancel()方法重新执行定时任务

如下示例,构造一个timer定时任务,
当访问页面时,等待0.2秒获取页面的截图,
再等待1秒后获取页面的截图
第一次获取页面的截图页面还没加载出来,
所以获取到的是空白页

function main(splash, args)
  local pngs = {}
  local timer = splash:call_later(function()
    pngs['a'] = splash:png()
    splash:wait(1)
    pngs['b'] = splash:png()
    end, 0.2)
  splash:go("https://www.mi.com")
  return pngs
end

其他方法

  • 获取页面源码
    splash:html()
    
  • 控制页面的等待时间
    splash:wait(2)
    
  • 设置请求头
    splash:set_user_agent('Splash')
    
  • 设置自定义请求头
    splash:set_custom_headers({
          ["User-Agent"] = "Splash",
          ["Host"] = "Splash.org"
        })
    
  • 获取当前页面的大小,即宽高
    splash:get_viewport_size()
    
  • 设置当前浏览器页面的大小,即宽高
    splash:set_viewport_size(400, 400)
    
  • 用来设置浏览器全屏显示
    splash:set_viewport_full()
    
  • 获取当前正在访问页面的url
    splash:url()
    
  • 获取页面加载过程描述
    splash:har()
    
  • 获取网页页面png格式或jpeg格式的截图
    params={
        png = splash:png(),
        jpeg = splash:jpeg()
    }
    
  • 设置页面的内容
    splash:set_content("<h1>hello</h1>")
    
  • 控制页面的上下左右滚动 用x定位左右,y定位上下
    splash.scroll_position = {y = 800}
    
  • 控制浏览器插件(如flash等)是否开启 默认为false
    splash.plugins_enabled= true
    
  • 页面图片是否加载,默认为true
    splash.images_enabled = false 
    
  • 页面加载超时时间,单位是秒
    splash.resource_timeout = 0.01 
    
  • 页面JavaScript的执行开关,默认为true
    splash.js_enabled = False
    
  • 模拟鼠标点击操作 传入x和y进行点击操作 查找到相关节点,调用此方法进行点击操作
    search = splash:select('#su')
    search:mouse_click()
    
  • 输入文字
    splash:select("#username"):send_text("username")
    

对接scrapy

def start_requests(self):
    lua="""
        function main(splash, args)
          splash.images_enabled = false  
          assert(splash:go(args.url))
          assert(splash:wait(1))
          js = string.format("document.querySelector('body > div.container > div.main.clearfix > div > div.page > span:nth-child(4) > a').click();", args.page)
          splash:runjs(js)
          assert(splash:wait(5))
          return splash:html()
        end
        """
    url="http://www.pingtan.gov.cn/jhtml/cn/8423"
    
    yield scrapy_splash.SplashRequest(
          url=url,
          endpoint="execute",
          args={
              "url":url,
              "lua_source":lua,
              "page":page,
              "wait":1
          },
          callback=self.parse
      )

Splash使用代理

直接设置

yield SplashRequest(
            url=self.start_urls[0],
            callback=self.parse,
            args={
                "wait": 3,
                 # 这里写你代理
                "proxy": 'http://xxx.xxx.xxx.xxx:xxx'
            }
 
        )

使用中间件设置

class ProxyMiddleware(object):
      def process_request(self, request, spider):
              # proxyServer 是代理
	      request.meta['splash']['args']['proxy'] = proxyServer	
              # 认证消息,没有可以不写
	      # request.headers["Proxy-Authorization"] = proxyAuth

好了就介绍到这里把 也是自己整理了好多大神的资料