zoukankan      html  css  js  c++  java
  • python3编写网络爬虫15-Splash的使用

    Splash是一个JavaScript渲染服务 是一个带有HTTP API的轻量级浏览器 同时对接了python的Twisted 和QT库

    利用它可以实现对动态渲染页面的抓取

    功能介绍

    1.异步方式处理多个网页渲染过程
    2.获取渲染后的页面源代码或截图
    3.通过关闭图片渲染或使用Adblock规则加快页面渲染速度
    4.可执行特定js脚本
    5.可通过Lua脚本来控制页面渲染过程
    6.获取渲染的详细过程并通过HAR(HTTP Archive)格式呈现

    安装准备
    1.Docker的安装 (后面讲到时会详细讲 这里先安装)

    windows下安装:
    win10 64位 推荐 Docker for windwos 官网下载最新安装包:

    https://docs.docker.com/docker-for-windows/install/

    不是 64位的 下载 Docker Toolbox :

     https://docs.docker.com/toolbox/toolbox_install_windows/

    下载后双击安装 安装完成后 进入命令行 输入docker 运行没有报错证明安装成功了

    2.安装splash 命令行执行 

    docker run -p 8050:8050 scrapinghub/splash

    显示如下省略部分表示服务启动了
    [-] Site starting on 8050
    [-] Starting factory <twisted.web.server.Site object at 0x7fb62b1957f0>

    打开浏览器 访问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进行对接,
    所有网页的动态渲染、模拟点击、表单提交、页面滑动、延时等待后的一些结果均可以自由控制。

  • 相关阅读:
    python学习-流程控制(四)
    如何将子分支代码推送到新建的仓库
    python学习-列表、元组和字典(三)
    jmeter-操作mysql
    jmeter-控制业务比例
    python-使用session记录登录状态
    python-json与字典的区别
    cookie session token 详解
    python-装饰器
    unittest断言
  • 原文地址:https://www.cnblogs.com/liuxiaosong/p/10364060.html
Copyright © 2011-2022 走看看