Python3网络爬虫实战-38、动态渲染页面抓取:Splash的使用

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

1. 功能介绍

利用 Splash 我们可以实现如下功能:

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

接下来我们来了解一下它的具体用法。

2. 准备工作

在本节开始之前请确保已经正确安装好了 Splash 并可以正常运行服务,如没有安装可以参考第一章的安装说明。

3. 实例引入

首先我们可以通过 Splash 提供的 Web 页面来测试 Splash的渲染过程,例如我们在本机 8050 端口运行了 Splash 服务,打开:http://localhost:8050/ 即可看到其 Web 页面,如图 7-6 所示:

图 7-6 Web 页面
在右侧呈现的是一个渲染示例,我们可以看到在上方有一个输入框,默认是:http://google.com,我们在这里换成百度测试一下,将内容更改为:https://www.baidu.com,然后点击按钮,开始渲染,结果如图 7-7 所示:

图 7-7 运行结果
可以看到网页的返回结果呈现了渲染截图、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
Python资源分享qun 784758214 ,内有安装包,PDF,学习视频,这里是Python学习者的聚集地,零基础,进阶,都欢迎

这个脚本是实际是 Lua 语言写的脚本,Lua也是一门编程语言,简洁易用。
即使我们不懂这个语言的语法,但通过脚本表面意思我们也可以大致了解到它是首先调用 go() 方法去加载页面,然后调用 wait() 方法等待了一定的时间,最后返回了页面的源码、截图和 HAR 信息。
所以到这里我们可以大体了解到 Splash 是通过 Lua 脚本来控制了页面的加载过程,加载过程完全模拟浏览器,最后可返回各种格式的结果,如网页源码、截图等。
所以接下来我们要学会用 Splash 的话,一是需要了解其中 Lua 脚本的写法,二是需要了解相关 API 的用法,那么接下来我们就来了解一下这两部分内容。

4. 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/ 的代码编辑区域,然后点击按钮来测试一下。
这样我们就会看到其返回了网页的标题,这里我们是通过 evaljs() 方法传入 JavaScript 脚本,而 document.title 的执行结果就是返回网页标题,执行完毕后赋值给一个 title 变量,随后将其返回,这样就可以看到其返回结果就是网页标题了,如图 7-8 所示:

图 7-8 运行结果
注意到我们在这里定义的方法名称叫做 main(),这个名称必须是固定的,Splash 会默认调用这个方法。
方法的返回值可以是字典形式、也可以是字符串形式,最后都会转化为一个 Splash HTTP Response,例如:

function main(splash)
    return {hello="world!"}
end

这样即返回了一个字典形式的内容。

function main(splash)
    return 'hello'
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

运行后的返回结果是三个站点的截图,如图 7-9 所示:

图 7-9 运行结果
在脚本内调用了 wait() 方法,这类似于 Python 中的 sleep(),参数为等待的秒数,当 Splash 执行到此方法时,它会转而去处理其他的任务,然后在指定的时间过后再回来继续处理。
在这里值得注意的是 Lua 脚本中的字符串拼接,和 Python不同,这里的字符串拼接使用的是 … 操作符,而不是 +,如有必要可以简单了解一下Lua脚本的语法,链接:http://www.runoob.com/lua/lua…。
另外这里我们做了加载时的异常检测,go() 方法会返回加载页面的结果状态,如果页面出现 4XX 或 5XX 状态码,ok 变量就会为空,就不会返回加载后的图片。

5. Splash对象属性

我们注意到在前面的例子中 main() 方法的第一个参数是 splash,这个对象非常重要,类似于在 Selenium 中的WebDriver 对象:

from selenium import webdriver
browser = webdriver.Chrome()

如上所示,现在的 splash 对象就如同此处 Selenium 中的 browser 对象,我们可以调用它的一些属性和方法来控制加载过程,接下来我们首先看下它的属性。

args

splash 对象的 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.args.url
end

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,
    "type": "ScriptError",
    "info": {
        "type": "JS_ERROR",
        "js_error_message": null,
        "source": "[string \"function main(splash, args)\r...\"]",
        "message": "[string \"function main(splash, args)\r...\"]:4: unknown JS error: None",
        "line_number": 4,
        "error": "unknown JS error: None",
        "splash_method": "evaljs"
    },
    "description": "Error happened while executing Lua script"
}

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

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 秒之内没有得到响应就会抛出异常,错误如下:

{
    "error": 400,
    "type": "ScriptError",
    "info": {
        "error": "network5",
        "type": "LUA_ERROR",
        "line_number": 3,
        "source": "[string \"function main(splash)\r...\"]",
        "message": "Lua error: [string \"function main(splash)\r...\"]:3: network5"
    },
    "description": "Error happened while executing Lua script"
}

此属性适合在网页加载速度较慢的情况下设置,如果超过了某个时间无响应则直接抛出异常并忽略即可。

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

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

plugin

你可能感兴趣的:(python,数据挖掘,爬虫)