Class 17 - 2 动态渲染页面爬取 — Splash

一、Splash 的使用

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

  1. 实例引入

    • 通过 Splash 提供的 Web 页面来测试其渲染过程。例:在本机 8050 端口上运行 Splash 服务,打开 http://localhost:8050/ 即可看到其 Web 页面:
    • 黑色框显示的是一个渲染示例。上方有个输入框,默认是 http://google.com, 换成百度测试,将内容更改为 https://www.baidu.com,点击 Render me 按钮开始渲染。 结果:
    • 网页的返回结果呈现了渲染截图、HAR 加载统计数据、网页的源代码。
    • 通过 HAR 的结果可以看到,Splash 执行整个网页的渲染过程,包括 CSS、JavaScript 的加载等过程,呈现的页面和浏览器中得到的结果完全一致。
    • 这个过程由什么来控制呢?重新返回首页,可以看到有段脚本,内容:

      function main(splash, args)
        assert(splash:go(args.url))
        assert(splash:wait(0.5))
        return {
          html = splash:html(),
          png = splash:png(),
          har = splash:har(),
        }
      end

      这个脚本是用 Lua 语言写的脚本。从脚本的表面意思,它首先调用 go()方法去加载页面,再调用 wait()方法等待一定时间,最后返回页面的源码、截图和 HAR 信息。

    • Splash 通过 Lua 脚本来控制了页面加载过程,加载过程完全模拟浏览器,最后可返回各种格式的结果,如:网页源码和截图等。
  2. Splash Lua脚本
    • Splash 可以通过 Lua 脚本执行一系列渲染操作,这样就可以用 Splash 来模拟类似 Chrome、PhantomJS 的操作了。(Splash Lua 脚本的人口和执行方式)
    • 入口及返回值
      • 实例:

        function main(splash, args)
          splash:go("http://www.baidu.com")
          splash:wait(0.5)
            local title = splash:evaljs("document.title")
            return {title= title}
        end

        将代码贴到 http://localhost:8050/ 的代码编辑区域,点击 Render me!按钮 测试

      • 它返回网页的标题,这里通过 evaljs()方法传人 JavaScript 脚本,而 document.title 的执行结果就是返回网页标题,执行完毕后将其赋值给一个 title 变量,随后将其返回:

        注意,在这里定义的方法名称叫作 main()。这个名称必须是固定的,Splash 会默认调用这个方法。
      • 该方法的返回值既可以是字典形式,也可以是字符串形式,最后都会转化为 Splash HTTP Response,例如:

        function main(splash)
          return { hello="world"  }
        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
            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 执行到此方法时,它会转而去处理其他任务,然后在指定的时间过后再回来继续处理。
        • 注意:Lua 脚本中的字符串拼接和 Python 不同,它使用的是..操作符,而不是+。简单了解 Lua 脚本的语法:http://www.runoob.com/lua/lua-basic-syntax.html。
        • 这里做了加载时的异常检测。go()方法会返回加载页面的结果状态,如果页面州现 4xx 或 5xx 状态码,ok 变量就为空,不会返回加载后的图片。
  3. Splash 对象属性
    • 前面例子中 main()方法的第一个参数是 splash,这个对象非常重要,类似于 Selenium 中的 WebDriver 对象,可以调用它的一些属性和方法来控制加载过程。
    • args
      • args 属性可以获取加载时配置的参数,如 URL,如果为 GET 请求,还可以获取 GET 请求参数;如果为 POST 请求,可以获取表单提交的数据。Splash 也支持使用第二个参数直接作为 args,例:

        function main(splash, args)
          local url = args.url
        end

        这里第二个参数 args 就相当于 splash.args 属性,以上代码等价于:

        function main(splash)
          local url = splash.url
        end
    • js_enabled
      • 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

        接着,重新调用 evaljs()方法执行 JavaScript 代码,此时运行结果就会抛出异常:

        {
            "error": 400,
            "info": {
                "line_number": 1,
                "error": "‘)‘ expected near char(239)",
                "source": "[string \"function main(splash, args)\r...\"]",
                "message": "[string \"function main(splash, args)\r...\"]:1: ‘)‘ expected near char(239)",
                "type": "LUA_INIT_ERROR"
            },
            "description": "Error happened while executing Lua script",
            "type": "ScriptError"
        }

        一般来说,不用设置此属性,默认开启即可。

    • 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

        例,这里将超时时间设置为 0.1秒。如果在 0.1 秒之内没有得到响应,就会抛出异常。 此属性适合在网页加载速度较慢的情况下设置,如果超过了某个时间无响应,则直接抛出异常并忽略即可。

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

          这样返回的页面截图就不会带有图片,加载速度也会快很多

    • plugins_enabled
      • plugins_enabled 属性可以控制浏览器插件(如 Flash 插件)是否开启。默认情况下,此属性是 false,表示不开启。可以使用如下代码控制其开启和关闭:

        splash。plugins_enabled = true/false
    • scroll_position  
      • 设置 scroll_position 属性,可以控制页面上下或左右滚动。是一个比较常用的属性,示例如下:

        function main(splash, args)
          assert(splash:go(‘https://www.taobao.com‘))
          splash.scroll_position = {y=400}
          return {png=splash:png()}
        end

        这样我们就可以控制页面向下滚动 400 像素值。

      • 如果要让页面左右滚动,可以传入 x 参数,代码:
          splash.scroll_position = {x=100,y=200}
  4. Splash 对象的方法
    • 除了前面介绍的属性外,Splash 对象还有如下方法:
    • go()
      • go() 方法用来请求某个链接,而且它可以模拟 GET 和 POST 请求,同时支持传入请求头、表单等数据,用法:

        ok, reason = spalsh:go{url, baseurl=nil,headers=nil,http_method="GET",body=nil,formdata=nil}

        参数说明:

        • url:请求的URL\
        • baseurl:可选参数,默认为空,表示自愿加载相对路径。
        • headers:可选参数,默认为空,表示请求头
        • http_method:可选参数,默认为空,发POST请求时的表单数据,使用Content-type 为application/json
        • formdata:可选参数,默认为空,POST的时候的表单数据,使用的Content-type为application/x-www-form-urlencoded、
      • 该方法的返回结果是结果 ok 和原因 reason 的组合,如果 ok 为空,代表网页加载出现了错误,此时 reason 变量中包含了错误的原因,否则证明页面加载成功。示例:
        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

        这里模拟了 POST 请求,并传入了 POST 的表单数据,如果成功,则返回页面的源代码。运行结果:

        <html><head></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">{
          "args": {},
          "data": "",
          "files": {},
          "form": {
            "name": "Germey"
          },
          "headers": {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "en,*",
            "Connection": "close",
            "Content-Length": "11",
            "Content-Type": "application/x-www-form-urlencoded",
            "Host": "httpbin.org",
            "Origin": "null",
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1"
          },
          "json": null,
          "origin": "180.175.116.149",
          "url": "http://httpbin.org/post"
        }
        </pre></body></html>

        成功实现POST请求并发送了表达数据。

    • wait()
      • 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 的组合。示例:
        function main(splash)
          splash:go("https://www.taobao.com")
          splash:wait(2)
          return {html=splash:html()}
        end

        该功能实访问淘宝并等待2秒,随后返回页面源代码的功能。

    • jsfunc()
      • 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
        输出:
        Splash Response: "There are 22 DIVs"

        首先,声明了一个 JavaScript 定义的方法,然后在页面加载成功后调用此方法计算出页面中 div 节点的个数。

      • 关于 JavaScript 到 Lua 脚本的更多转换细节,参考官方文档: https://splash.readthedocs.io/en/ stable/scripting-ref.html#splash-jsfunc。
    • evaljs()
      • evaljs() 方法可以执行 JavaScript 代码并返回最后一条 JavaScript 语句的返回结果,使用方法: 

        result = splash:evaljs(js)

        如,可以用下面的代码来获取页面标题:

        local title = splash:evaljs("document.title")
    • runjs()
      • 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
        输出:
        bar

        用 runjs()先声明一个 JavaScript 定义的方法,通过 evaljs()来调用得到的结果。

    • autoload()
      • autoload() 方法可以设置每个页面访问时自动加载的对象,使用方法:

        ok, reason = splash:autoload{source_or_url, source=nil, url=nil}

        参数说明:    

        • source_or_url:JavaScript代码或者JavaScript库链接
        • source:JavaScript代码
        • url:JavaScript库链接
      • autoload() 方法只负责加载 JavaScript 代码或库,不执行任何操作。如果要执行操作,可以调用 evaljs() 或 runjs()方法。例:
        function main(splash,args)
          splash:autoload([[
            function get_document_title(){
                return document.title;
                  }
            ]])
          splash:go("https://www.baidu.com")
          return splash:evaljs("get_document_title()")
        end

        调用 autoload()方法声明一个 JavaScript 方法,然后通过 evaljs ()方法来执行此 JavaScript 方法。输出百度页面文本。

      • 另外,也可以使用 autoload()方法加载某些方法库,如 jQuery,示例:
        function main(splash, args)
          assert(splash:autoload("https://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 

        输出:"JQuery version:2.1.3"

    • call_later()
      • 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.05)
          splash:go("https://www.taobao.com")
          splash:wait(3.0)
          return snapshots
        end

        这里设置了一个定时任务,0.05 秒的时候获取网页截图,然后等待 1 秒,1.05 秒时再次获取网页截图,访问的页面是淘宝,最后将截图结果返回。 结果将是:第一次截图时网页还没有加载出来,第二次网页加载成功。

    • http_get()
      • 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.statsus
          }
        end

        输出结果:

        Splash Response:Object
        html:String(length 355)
        {
          "args": {},
          "headers": {
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "en,*",
            "Connection": "close",
            "Host": "httpbin.org",
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1"
          },
          "origin": "180.175.116.149",
          "url": "http://httpbin.org/get"
        }

    • http_post()
      • 与 http_get()方法类似,用来模拟发送 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

        输出:

        Splash Response:Object
        html:String(length 535)
        {
          "args": {},
          "data": "{\"name\": \"Germey\"}",
          "files": {},
          "form": {},
          "headers": {
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "en,*",
            "Connection": "close",
            "Content-Length": "18",
            "Content-Type": "application/json",
            "Host": "httpbin.org",
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1"
          },
          "json": {
            "name": "Germey"
          },
          "origin": "180.175.116.149",
          "url": "http://httpbin.org/post"
        }

        这里成功模拟提交POST请求并发送表单数据。

    • set_content()
      • set_content() 方法用来设置页面内容,示例:

        function main(splash)
          assert(splash:set_content("<html><body><h1>hello</h1></body></html>"))
          return splash:png()
        end
    • html()
      • html() 方法用来获取网页的源代码。示例:

        function main(splash,args)
          splash:go("https://httpbin.org/get")
          return splash:html()
        end
        输出:

        <html><head></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">{
          "args": {},
          "headers": {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "en,*",
            "Connection": "close",
            "Host": "httpbin.org",
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1"
          },
          "origin": "180.175.116.149",
          "url": "https://httpbin.org/get"
        }
        </pre></body></html>

    • png()
      • png() 方法用来获取 PNG 格式的网页截图,示例:

        function main(splash,args)
          splash:go("https://www.taobao.com")
          return splash:png()
        end
    • jpeg()  
      • jpeg() 方法用来获取 JPEG 格式的网页截图,示例:

        function main(splash,args)
          splash:go("https://www.taobao.com")
          return splash:jpeg()
        end
    • har()
      • har() 方法用来获取页面加载过程描述,示例:

        function main(splash, args)
          splash:go("https://www.baidu.com")
          return splash:har()
        end

        输出结果:

    • url()
      • url()方法可以获取当前正在访问的 URL,示例:

        function main(splash,args)
          splash:go("https://www.baidu.com")
          return splash:url()
        end
        输出:
        "https://www.baidu.com/"
    • get_cookies()
      • get_cookies() 方法可以获取当前页面的 Cookies,示例:

        function main(splash, args)
          splash:go("https://www.baidu.com")
          return splash:get_cookies()
        end
        输出:

        Splash Response: Array[7]
        0: Object
        domain: ".baidu.com"
        expires: "2087-01-21T09:30:08Z"
        httpOnly: false
        name: "BAIDUID"
        path: "/"
        secure: false
        value: "70BACA9750D3C6FBE7DA3EDC2B9E0FCE:FG=1"
        1: Object
        domain: ".baidu.com"
        expires: "2087-01-21T09:30:08Z"
        httpOnly: false
        name: "BIDUPSID"
        path: "/"
        secure: false
        value: "70BACA9750D3C6FBE7DA3EDC2B9E0FCE"
        2: Object
        domain: ".baidu.com"
        expires: "2087-01-21T09:30:08Z"
        httpOnly: false
        name: "PSTM"
        path: "/"
        secure: false
        value: "1546496161"
        3: Object
        domain: ".baidu.com"
        httpOnly: false
        name: "delPer"
        path: "/"
        secure: false
        value: "0"
        4: Object
        domain: "www.baidu.com"
        httpOnly: false
        name: "BD_HOME"
        path: "/"
        secure: false
        value: "0"
        5: Object
        domain: ".baidu.com"
        httpOnly: false
        name: "H_PS_PSSID"
        path: "/"
        secure: false
        value: "1456_21095_28206_28131_26350_28139_27543"
        6: Object
        domain: "www.baidu.com"
        expires: "2019-01-13T06:16:01Z"
        httpOnly: false
        name: "BD_UPN"
        path: "/"
        secure: false
        value: "143354"

    • add_cookie()
      • 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()
      • clear_cookies() 方法可以清除所有的 Cookies,示例:

        function main(splash)
          splash:go("https://www.baidu.com")
          splash:clear_cookies()
          return splash:get_cookies()
        end

        清除了所有的 Cookies,然后调用 get_cookies()将结果返回。 输出:Splash Response: Array[0], Cookies 被全部清空。

    • get_viewport_size()  
      • get_viewport_size() 方法可以获取当前浏览器页面的大小,即宽高,示例:

        function main(splash)
          splash:go("https://www.baidu.com/")
          return splash:get_viewport_size()
        end

        输出:

        Splash Response: Array[2]
        0: 1024
        1: 768
    • set_viewport_size()
      • set_viewport_size() 方法可以设置当前浏览器页面的大小,即宽高,用法:

        splash:set_viewport_size(width,height)

        例:访问一个宽度自适应的页面:

        function main(splash)
          splash:set_viewport_size(400,700)
          assert(splash:go("https://cuiqingcai.com"))
          return splash:png()
        end
    • set_viewport_full() 
      • set_viewport_full() 方法可以设置浏览器全屏显示,示例:

        function main(splash)
          splash:set_viewport_full()
          assert(splash:go("https://www.cuiqingcai.com"))
          return splash:png()
        end
    • set_user_agent()
      • 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. 结果:User-agent 被设置成功。

    • set_custom_headers()
      • 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 属性,输出结果中User-Agent 和Site被更改。

    • select()
      • 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()
      • 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()
      • 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() 方法提交查询,然后页面等待3秒,返回截图。成功获取了查询后的页面内容,模拟了百度搜索操作。

    • Splash 对象的所有 API 操作:详细说明参见官方文档 https://splash.readthedocs.io/en/stable/scripting-ref.html 。
    • 针对页面元素 API 操作:https://splash.readthedocs.io/en/stable/scripting-element-object.html。   
  5. Splash API 调用
    • Splash Lua 脚本是在 Splash 页面中测试运行的,Splash 提供了一些 HTTPAPI 接口,只需请求这些接口并传递相应的参数即可。就可以利用 Splash 渲染页面 和 Python 程序结合使用并抓取 JavaScript 渲染的页面。
    • render.html
      • 接口用于获取 JavaScript 渲染的页面的 HTML 代码,接口地址是 Splash 的运行地址加接口名称,例: http://localhost:8050/render.html。用 curl 来测试一下:

        curl http://localhost:8050/render.html?url=https://www.baidu.com

        给此接口传递了一个 url 参数来指定渲染的 URL,返回结果即页面渲染后的源代码。用 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)

        此时得到响应的时间就会变长,如这里会等待 5 秒多钟才能获取淘宝页面的源代码。

      • 接口还支持代理设置、图片加载设置、Header 设置、请求方法设置,具体的用法见官方文档:https://splash.readthedocs.io/en/stable/api.html#render-html。
    • render.png
      • 接口可以获取网页截图,其参数比 render.html 多几个,如通过 width 和 height 来控制宽高,返回的是 PNG 格式的图片二进制数据。示例:

        curl http://localhost:8050/render.png?url=https://www.taobao.com&wait=5&width=1000&height=700

        这里传入 width 和 height 来设置页面大小为 1000 像素×700 像素。

      • 如果用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(‘taobao.png‘,‘wb‘) as f:
            f.write(response.content)

        这样就成功获取了京东首页渲染完成后的页面截图,详细的参数设置参考官网文档: https://splash.readthedocs.io/en/stable/api.html#render-png。

    • render.jpeg
      • render.jpej 接口和 render.png 类似,不过它返回的是 JPEG 格式的图片二进制数据。
      • 另外, 接口比 render.png 多了参数 quality,用来设置图片质量。
    • render.har()
      • 接口用于获取页面加载的 HAR 数据,示例:

        curl http://localhost:8050/render.har?url=https://www.jd.com&wait=5

        运回结果非常多,是一个 JSON 格式的数据,其中包含页面加载过程中的 HAR 数据

    • render.json
      • 接口包含前面接口的所有功能,返回结果是 JSON 格式,示例:

        curl http://localhost:8050/render.json?url=https://httpbin.org
      • 可以通过传人不同参数控制其返回结果。如:传人 html=1,返回结果即会增加源代码数据;传入 png=1 ,返回结果即会增加页面 PNG 截图数据;传入 har=1 ,则会获得页面 HAR 数据。例:
        curl http://localhost:8050/render.json?url=https://httpbin.org&html=1

        这样返回的 JSON 结果会包含网页源代码和 HAR 数据。

      • 更多参数设置参考官方文档: https://splash.readthedocs.io/en/stable/api.html#render-json。
    • excute
      • excute() 接口是最为强大的接口。前面说了很多 Splash Lua 脚本的操作,用此接口便可实现与 Lua 本的对接。  前面的 render.html 和 render.png 等接口对于一般的 JavaScript 渲染页面是足够了,但是如果要实现一些交互操作的话,它们还是无能为力,这里就需要使用 execute 接口。  先实现一个最简单的脚本,直接返回数据:

        function main(splash)
            return ‘hello‘
        end

        然后将脚本转化为 URL 编码后的字符串,拼接到 execute 接口后面,示例:

        curl http://localhost:8050/execute?lua_source=function+main%28splash%29%0D%0A++return+%27hello%27%0D%0Aend

        通过 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)
        输出:
        hello

        这里用 Python 中的三引号将 Lua 脚本包括起来,然后用 urllib.parse 模块里的 quote()方法将脚本进行 URL 转码,随后构造 Splash 请求 URL,将其作为 lua_source 参数传递,这样运行结果就会显示 Lua 脚本执行后的结果。

      • 实例:
        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)

        输出:

        {"html": "{\n  \"args\": {}, \n  \"headers\": {\n    \"Accept-Encoding\": \"gzip, deflate\", \n    \"Accept-Language\": \"en,*\", \n    \"Connection\": \"close\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1\"\n  }, \n  \"origin\": \"180.165.241.94\", \n  \"url\": \"http://httpbin.org/get\"\n}\n", "status": 200, "url": "http://httpbin.org/get"}

        返回结果是 JSON 形式,成功获取了请求的 URL, 状态码和网页源代码。

      • 之前所说 Lua 脚本均可以用此方式与 Python 进行对接,所有网页的动态渲染,模拟点击、表单提交、页面滑动、延时等待后的一些结果均可以自由控制,获取页面源码和截图也都ok。

原文地址:https://www.cnblogs.com/Mack-Yang/p/10195835.html

时间: 2024-10-09 16:35:54

Class 17 - 2 动态渲染页面爬取 — Splash的相关文章

爬虫动态渲染页面爬取之selenium驱动chrome浏览器的使用

Selenium是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样,可以用其进行网页动态渲染页面的爬取. 支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等. 1. 示例 ### selenium的使用 ''' Selenium是一个用于Web应用程序测试的工具. Selenium测试直接运行在浏览器中,就像真正的用户在操作一样. 支持的浏览器包括IE(7

python--Selenium(动态渲染页面爬取)

上一节,学习了Ajax,Ajax 其实也是JS 动态渲染的页面的一种形式,通过直接分析Ajax,仍然可以借助requests  或者 urllib 来实现数据爬取. 但是JS动态渲染的页面不止Ajax 一种:还有就是像淘宝这种页面,即使是Ajax 获取的数据,但是其Ajax 接口含有很多加密参数,我们直接很难找出其规律,,也就很难直接分析Ajax 来抓取. 为了解决这些问题,可以直接使用模拟浏览器运行的方式来实现,这样就可以做到在浏览器中看到是什么样,抓取的源码就是什么样,也就是可见及可爬.这样

第8章 动态渲染页面爬取

使用 Selenium 使用 Splash Splash 负载均衡 Selenium 爬取淘宝商品 原文地址:https://www.cnblogs.com/pzk7788/p/10536713.html

python爬虫14--Selenium动态渲染页面抓取

1.声明浏览器对象 from selenium import webdriver browser = webdriver.Chrome() # browser = webdriver.Firefox() # browser = webdriver.Edge() 2.访问页面 from selenium import webdriver browser = webdriver.Chrome() url = 'http://www.baidu.com' browser.get(url) print(

python爬爬爬之单网页html页面爬取

python爬爬爬之单网页html页面爬取 作者:vpoet 日期:大约在夏季 注:随意copy 不用告诉我 #coding:utf-8 import urllib2 Response=urllib2.urlopen("http://www.baidu.com"); Html=Response.read(); print Html; 运行结果: 再看看百度的源码: 是一样,没骗你吧

Python网络爬虫技巧小总结,静态、动态网页轻松爬取数据

很多人学用python,用得最多的还是各类爬虫脚本:有写过抓代理本机验证的脚本,有写过自动收邮件的脚本,还有写过简单的验证码识别的脚本,那么我们今天就来总结下python爬虫抓站的一些实用技巧. 静态网页 对于静态网页的爬虫不用多说大家也都知道,因为爬取静态网页非常的简单,只要用requests直接把html爬取下来然后用正则表达式匹配就可以了. 动态网页 相对于静态网页的简单,但是动态网页的就会相对而而言会复杂一下,而且现在互联网的发展速度,动态网页是最多的,静态网页是比较少的,不过他有张良计

亚马逊商品页面爬取

通过来源审查,访问错误,不支持直接访问: 更改头信息,重新定义 user-agent,模拟浏览器(Mozilla/5.0浏览器标识字段,页面信息不全,可以成功访问): 全代码: import requests      url="https://www.amazon.cn/?tag=baidu250-23&hvadid={creative}&ref=pz_ic_22fvxh4dwf_e"        try: kv={'user-agent':'Mozilla/5.0

微信小程序 修改数据,并动态渲染页面;修改数组;

一.修改数据,并在页面动态渲染 this.setData({ txt: '12112' }) 二.修改数组 var rotateClassItem = 'rotateClass['+ index + ']'; that.setData({ [rotateClassItem]: !that.data.rotateClass[index] }) 原文地址:https://www.cnblogs.com/siyecao2010/p/10083896.html

2:url有规律的多页面爬取

举例网站:http://www.luoo.net/music/期刊号 e.g:http://www.luoo.net/music/760 打算爬取其title:Hello World:pic:desc:本期音乐为......<8-bit Love>. 步骤: 1):建立项目 在shell中你对应的目录下:scrapy startproject luoo 在pycharm中打开luoo文件夹 2):编写items.py import scrapyclass LuooItem(scrapy.Ite