Splash是一个JavaScript渲染服务,是一个带有HTTP API的轻量级浏览器,同时它对接了Python中的Twisted和QT库。利用它,咱们一样能够实现动态渲染页面的抓取。html
利用Splash,咱们能够实现以下功能:node
接下来,咱们来了解一下它的具体用法。jquery
在开始以前,请确保已经正确安装好了Splash并能够正常运行服务。若是没有安装,能够参考第1章。json
首先,经过Splash提供的Web页面来测试其渲染过程。例如,咱们在本机8050端口上运行了Splash服务,打开http://localhost:8050/便可看到其Web页面,如图7-6所示。api
图7-6 Web页面浏览器
在图7-6右侧,呈现的是一个渲染示例。能够看到,上方有一个输入框,默认是google.com,这里换成百度测试一下,将内容更改成www.baidu.com,而后点击Render me按钮开始渲染,结果如图7-7所示。缓存
图7-7 运行结果bash
能够看到,网页的返回结果呈现了渲染截图、HAR加载统计数据、网页的源代码。微信
经过HAR的结果能够看到,Splash执行了整个网页的渲染过程,包括CSS、JavaScript的加载等过程,呈现的页面和咱们在浏览器中获得的结果彻底一致。cookie
那么,这个过程由什么来控制呢?从新返回首页,能够看到其实是有一段脚本,内容以下:
123456789function 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脚原本控制了页面的加载过程的,加载过程彻底模拟浏览器,最后可返回各类格式的结果,如网页源码和截图等。
接下来,咱们就来了解Lua脚本的写法以及相关API的用法。
Splash能够经过Lua脚本执行一系列渲染操做,这样咱们就能够用Splash来模拟相似Chrome、PhantomJS的操做了。
首先,咱们来了解一下Splash Lua脚本的入口和执行方式。
首先,来看一个基本实例:
123456function 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!按钮来测试一下。
咱们看到它返回了网页的标题,如图7-8所示。这里咱们经过evaljs()
方法传入JavaScript脚本,而document.title
的执行结果就是返回网页标题,执行完毕后将其赋值给一个title
变量,随后将其返回。
图7-8 运行结果
注意,咱们在这里定义的方法名称叫做main()
。这个名称必须是固定的,Splash会默认调用这个方法。
该方法的返回值既能够是字典形式,也能够是字符串形式,最后都会转化为Splash HTTP Response,例如:
123function main(splash) return {hello="world!"}end复制代码
返回了一个字典形式的内容。例如:
123function 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
复制代码
运行结果是3个站点的截图,如图7-9所示。
图7-9 运行结果
在脚本内调用的wait()
方法相似于Python中的sleep()
,其参数为等待的秒数。当Splash执行到此方法时,它会转而去处理其余任务,而后在指定的时间事后再回来继续处理。
这里值得注意的是,Lua脚本中的字符串拼接和Python不一样,它使用的是..
操做符,而不是+
。若是有必要,能够简单了解一下Lua脚本的语法,详见www.runoob.com/lua/lua-bas…。
另外,这里作了加载时的异常检测。go()
方法会返回加载页面的结果状态,若是页面出现4xx或5xx状态码,ok
变量就为空,就不会返回加载后的图片。
咱们注意到,前面例子中main()
方法的第一个参数是splash
,这个对象很是重要,它相似于Selenium中的WebDriver
对象,咱们能够调用它的一些属性和方法来控制加载过程。接下来,先看下它的属性。
该属性能够获取加载时配置的参数,好比URL,若是为GET请求,它还能够获取GET请求参数;若是为POST请求,它能够获取表单提交的数据。Splash也支持使用第二个参数直接做为args
,例如:
123function main(splash, args) local url = args.urlend复制代码
这里第二个参数args
就至关于splash.args
属性,以上代码等价于:
123function main(splash) local url = splash.args.urlend复制代码
js_enabled复制代码
这个属性是Splash的JavaScript执行开关,能够将其配置为true
或false
来控制是否执行JavaScript代码,默认为true
。例如,这里禁止执行JavaScript代码:
123456function 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代码,此时运行结果就会抛出异常:
1234567891011121314{ "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"}复制代码
不过通常来讲,不用设置此属性,默认开启便可。
此属性能够设置加载的超时时间,单位是秒。若是设置为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秒以内没有获得响应,就会抛出异常,错误以下:
123456789101112{ "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"}复制代码
此属性适合在网页加载速度较慢的状况下设置。若是超过了某个时间无响应,则直接抛出异常并忽略便可。
此属性能够设置图片是否加载,默认状况下是加载的。禁用该属性后,能够节省网络流量并提升网页加载速度。可是须要注意的是,禁用图片加载可能会影响JavaScript渲染。由于禁用图片以后,它的外层DOM节点的高度会受影响,进而影响DOM节点的位置。所以,若是JavaScript对图片节点有操做的话,其执行就会受到影响。
另外值得注意的是,Splash使用了缓存。若是一开始加载出来了网页图片,而后禁用了图片加载,再从新加载页面,以前加载好的图片可能还会显示出来,这时直接重启Splash便可。
禁用图片加载的示例以下:
1
2
3
4
5
|
function main(splash, args)
splash.images_enabled = false
assert(splash:go('https://www.jd.com'))
return {png=splash:png()}
end
|
这样返回的页面截图就不会带有任何图片,加载速度也会快不少。
此属性能够控制浏览器插件(如Flash插件)是否开启。默认状况下,此属性是false
,表示不开启。可使用以下代码控制其开启和关闭:
1
|
splash.plugins_enabled = true/false
|
经过设置此属性,咱们能够控制页面上下或左右滚动。这是一个比较经常使用的属性,示例以下:
1
2
3
4
5
|
function main(splash, args)
assert(splash:go('https://www.taobao.com'))
splash.scroll_position = {y=400}
return {png=splash:png()}
end
|
这样咱们就能够控制页面向下滚动400像素值,结果如图7-10所示。
图7-10 运行结果
若是要让页面左右滚动,能够传入x
参数,代码以下:
1
|
splash.scroll_position = {x=100, y=200}
|
除了前面介绍的属性外,Splash对象还有以下方法。
该方法用来请求某个连接,并且它能够模拟GET和POST请求,同时支持传入请求头、表单等数据,其用法以下:
1
|
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
变量中包含了错误的缘由,不然证实页面加载成功。示例以下:
123456function main(splash, args) local ok, reason = splash:go{"http://httpbin.org/post", http_method="POST", body="name=Germey"} if ok then return splash:html() endend复制代码
这里咱们模拟了一个POST请求,并传入了POST的表单数据,若是成功,则返回页面的源代码。
运行结果以下:
1234567891011121314151617181920212223<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": "60.207.237.85", "url": "http://httpbin.org/post"}</pre></body></html>复制代码
能够看到,咱们成功实现了POST请求并发送了表单数据。
此方法能够控制页面的等待时间,使用方法以下:
1
|
ok, reason = splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}
|
参数说明以下。
time
:等待的秒数。cancel_on_redirect
:可选参数,默认为false
,表示若是发生了重定向就中止等待,并返回重定向结果。cancel_on_error
:可选参数,默认为false
,表示若是发生了加载错误,就中止等待。返回结果一样是结果ok
和缘由reason
的组合。
咱们用一个实例感觉一下:
12345function main(splash) splash:go("https://www.taobao.com") splash:wait(2) return {html=splash:html()}end复制代码
这能够实现访问淘宝并等待2秒,随后返回页面源代码的功能。
此方法能够直接调用JavaScript定义的方法,可是所调用的方法须要用双中括号包围,这至关于实现了JavaScript方法到Lua脚本的转换。示例以下:
123456789101112function 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复制代码
运行结果以下:
1
|
There are 21 DIVs
|
首先,咱们声明了一个JavaScript定义的方法,而后在页面加载成功后调用了此方法计算出了页面中div
节点的个数。
关于JavaScript到Lua脚本的更多转换细节,能够参考官方文档:splash.readthedocs.io/en/stable/s…。
此方法能够执行JavaScript代码并返回最后一条JavaScript语句的返回结果,使用方法以下:
1
|
result = splash:evaljs(js)
|
好比,能够用下面的代码来获取页面标题:
1
|
local title = splash:evaljs("document.title")
|
此方法能够执行JavaScript代码,它与evaljs()
的功能相似,可是更偏向于执行某些动做或声明某些方法。例如:
1
2
3
4
5
6
|
function main(splash, args)
splash:go("https://www.baidu.com")
splash:runjs("foo = function() { return 'bar' }")
local result = splash:evaljs("foo()")
return result
end
|
这里咱们用runjs()
先声明了一个JavaScript定义的方法,而后经过evaljs()
来调用获得的结果。
运行结果以下:
1
|
bar
|
此方法能够设置每一个页面访问时自动加载的对象,使用方法以下:
1
|
ok, reason = splash:autoload{source_or_url, source=nil, url=nil}
|
参数说明以下。
source_or_url
:JavaScript代码或者JavaScript库连接。source
:JavaScript代码。url
:JavaScript库连接可是此方法只负责加载JavaScript代码或库,不执行任何操做。若是要执行操做,能够调用evaljs()
或runjs()
方法。示例以下:
1
2
3
4
5
6
7
8
9
|
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方法。
运行结果以下:
1
|
百度一下,你就知道
|
另外,咱们也可使用autoload()
方法加载某些方法库,如jQuery,示例以下:
1
2
3
4
5
6
|
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
|
运行结果以下:
1
|
JQuery version: 2.1.3
|
此方法能够经过设置定时任务和延迟时间来实现任务延时执行,而且能够在执行前经过cancel()
方法从新执行定时任务。示例以下:
1
2
3
4
5
6
7
8
9
10
11
|
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
|
这里咱们设置了一个定时任务,0.2秒的时候获取网页截图,而后等待1秒,1.2秒时再次获取网页截图,访问的页面是淘宝,最后将截图结果返回。运行结果如图7-11所示。
图7-11 运行结果
能够发现,第一次截图时网页尚未加载出来,截图为空,第二次网页便加载成功了。
此方法能够模拟发送HTTP的GET请求,使用方法以下:
1
|
response = splash:http_get{url, headers=nil, follow_redirects=true}
|
参数说明以下。
url
:请求URL。headers
:可选参数,默认为空,请求头。follow_redirects
:可选参数,表示是否启动自动重定向,默认为true
。示例以下:
1
2
3
4
5
6
7
8
9
|
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
|
运行结果以下:
12345678910111213141516Splash Response: Objecthtml: 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": "60.207.237.85", "url": "http://httpbin.org/get"}status: 200url: "http://httpbin.org/get"复制代码
和http_get()
方法相似,此方法用来模拟发送POST请求,不过多了一个参数body
,使用方法以下:
1
|
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复制代码
运行结果以下:
123456789101112131415161718192021222324Splash Response: Objecthtml: String (length 533){ "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": "60.207.237.85", "url": "http://httpbin.org/post"}status: 200url: "http://httpbin.org/post"复制代码
能够看到,这里咱们成功模拟提交了POST请求并发送了表单数据。
此方法用来设置页面的内容,示例以下:
1
2
3
4
|
function main(splash)
assert(splash:set_content("<html><body><h1>hello</h1></body></html>"))
return splash:png()
end
|
运行结果如图7-12所示。
图7-12 运行结果
此方法用来获取网页的源代码,它是很是简单又经常使用的方法。示例以下:
1
2
3
4
|
function main(splash, args)
splash:go("https://httpbin.org/get")
return splash:html()
end
|
运行结果以下:
1234567891011121314<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": "60.207.237.85", "url": "https://httpbin.org/get"}</pre></body></html>复制代码
此方法用来获取PNG格式的网页截图,示例以下:
1
2
3
4
|
function main(splash, args)
splash:go("https://www.taobao.com")
return splash:png()
end
|
此方法用来获取JPEG格式的网页截图,示例以下:
1
2
3
4
|
function main(splash, args)
splash:go("https://www.taobao.com")
return splash:jpeg()
end
|
此方法用来获取页面加载过程描述,示例以下:
1
2
3
4
|
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:har()
end
|
运行结果如图7-13所示,其中显示了页面加载过程当中每一个请求记录的详情。
图7-13 运行结果
此方法能够获取当前正在访问的URL,示例以下:
1
2
3
4
|
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:url()
end
|
运行结果以下:
1
|
https://www.baidu.com/
|
此方法能够获取当前页面的Cookies,示例以下:
1
2
3
4
|
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:get_cookies()
end
|
运行结果以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Splash Response: Array[2]
0: Object
domain: ".baidu.com"
expires: "2085-08-21T20:13:23Z"
httpOnly: false
name: "BAIDUID"
path: "/"
secure: false
value: "C1263A470B02DEF45593B062451C9722:FG=1"
1: Object
domain: ".baidu.com"
expires: "2085-08-21T20:13:23Z"
httpOnly: false
name: "BIDUPSID"
path: "/"
secure: false
value: "C1263A470B02DEF45593B062451C9722"
|
此方法能够为当前页面添加Cookie,用法以下:
1
|
cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}
|
该方法的各个参数表明Cookie的各个属性。
示例以下:
1
2
3
4
5
|
function main(splash)
splash:add_cookie{"sessionid", "237465ghgfsd", "/", domain="http://example.com"}
splash:go("http://example.com/")
return splash:html()
end
|
此方法能够清除全部的Cookies,示例以下:
1
2
3
4
5
|
function main(splash)
splash:go("https://www.baidu.com/")
splash:clear_cookies()
return splash:get_cookies()
end
|
这里咱们清除了全部的Cookies,而后调用get_cookies()
将结果返回。
运行结果以下:
1
|
Splash Response: Array[0]
|
能够看到,Cookies被所有清空,没有任何结果。
此方法能够获取当前浏览器页面的大小,即宽高,示例以下:
1
2
3
4
|
function main(splash)
splash:go("https://www.baidu.com/")
return splash:get_viewport_size()
end
|
运行结果以下:
1
2
3
|
Splash Response: Array[2]
0: 1024
1: 768
|
此方法能够设置当前浏览器页面的大小,即宽高,用法以下:
1
|
splash:set_viewport_size(width, height)
|
例如,这里访问一个宽度自适应的页面:
1
2
3
4
5
|
function main(splash)
splash:set_viewport_size(400, 700)
assert(splash:go("http://cuiqingcai.com"))
return splash:png()
end
|
运行结果如图7-14所示。
图7-14 运行结果
此方法能够设置浏览器全屏显示,示例以下:
1
2
3
4
5
|
function main(splash)
splash:set_viewport_full()
assert(splash:go("http://cuiqingcai.com"))
return splash:png()
end
|
此方法能够设置浏览器的User-Agent
,示例以下:
1
2
3
4
5
|
function main(splash)
splash:set_user_agent('Splash')
splash:go("http://httpbin.org/get")
return splash:html()
end
|
这里咱们将浏览器的User-Agent
设置为Splash
,运行结果以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<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": "Splash"
},
"origin": "60.207.237.85",
"url": "http://httpbin.org/get"
}
</pre></body></html>
|
能够看到,此处User-Agent
被成功设置。
此方法能够设置请求头,示例以下:
1
2
3
4
5
6
7
8
|
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
属性,运行结果以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<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",
"Site": "Splash",
"User-Agent": "Splash"
},
"origin": "60.207.237.85",
"url": "http://httpbin.org/get"
}
</pre></body></html>
|
该方法能够选中符合条件的第一个节点,若是有多个节点符合条件,则只会返回一个,其参数是CSS选择器。示例以下:
1
2
3
4
5
6
7
|
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()
方法填写了文本,而后返回网页截图。
结果如图7-15所示,能够看到,咱们成功填写了输入框。
图7-15 运行结果
此方法能够选中全部符合条件的节点,其参数是CSS选择器。示例以下:
1
2
3
4
5
6
7
8
9
10
11
|
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选择器选中了节点的正文内容,随后遍历了全部节点,将其中的文本获取下来。
运行结果以下:
1
2
3
4
5
6
7
8
9
10
11
|
Splash Response: Array[10]
0: "“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”"
1: "“It is our choices, Harry, that show what we truly are, far more than our abilities.”"
2: “There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”
3: "“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”"
4: "“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”"
5: "“Try not to become a man of success. Rather become a man of value.”"
6: "“It is better to be hated for what you are than to be loved for what you are not.”"
7: "“I have not failed. I've just found 10,000 ways that won't work.”"
8: "“A woman is like a tea bag; you never know how strong it is until it's in hot water.”"
9: "“A day without sunshine is like, you know, night.”"
|
能够发现,咱们成功地将10个节点的正文内容获取了下来。
此方法能够模拟鼠标点击操做,传入的参数为坐标值x
和y
。此外,也能够直接选中某个节点,而后调用此方法,示例以下:
1
2
3
4
5
6
7
8
9
|
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()
方法提交查询,而后页面等待三秒,返回截图,结果如图7-16所示。
图7-16 运行结果
能够看到,这里咱们成功获取了查询后的页面内容,模拟了百度搜索操做。
前面介绍了Splash的经常使用API操做,还有一些API在这再也不一一介绍,更加详细和权威的说明能够参见官方文档splash.readthedocs.io/en/stable/s…,此页面介绍了Splash对象的全部API操做。另外,还有针对页面元素的API操做,连接为splash.readthedocs.io/en/stable/s…。
前面说明了Splash Lua脚本的用法,但这些脚本是在Splash页面中测试运行的,如何才能利用Splash渲染页面呢?怎样才能和Python程序结合使用并抓取JavaScript渲染的页面呢?
其实Splash给咱们提供了一些HTTP API接口,咱们只须要请求这些接口并传递相应的参数便可,下面简要介绍这些接口。
此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称,例如http://localhost:8050/render.html。能够用curl
来测试一下:
1
|
curl http://localhost:8050/render.html?url=https://www.baidu.com
|
咱们给此接口传递了一个url
参数来指定渲染的URL,返回结果即页面渲染后的源代码。
若是用Python实现的话,代码以下:
1
2
3
4
|
import requests
url = 'http://localhost:8050/render.html?url=https://www.baidu.com'
response = requests.get(url)
print(response.text)
|
这样就能够成功输出百度页面渲染后的源代码了。
另外,此接口还能够指定其余参数,好比经过wait
指定等待秒数。若是要确保页面彻底加载出来,能够增长等待时间,例如:
1
2
3
4
|
import requests
url = 'http://localhost:8050/render.html?url=https://www.taobao.com&wait=5'
response = requests.get(url)
print(response.text)
|
此时获得响应的时间就会相应变长,好比这里会等待5秒多钟才能获取淘宝页面的源代码。
另外,此接口还支持代理设置、图片加载设置、Headers设置、请求方法设置,具体的用法能够参见官方文档splash.readthedocs.io/en/stable/a…。
此接口能够获取网页截图,其参数比render.html多了几个,好比经过width
和height
来控制宽高,它返回的是PNG格式的图片二进制数据。示例以下:
1
|
curl http://localhost:8050/render.png?url=https://www.taobao.com&wait=5&width=1000&height=700
|
这里咱们传入了width
和height
来设置页面大小为1000×700像素。
若是用Python实现,能够将返回的二进制数据保存为PNG格式的图片,具体以下:
1
2
3
4
5
6
|
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)
|
获得的图片如图7-17所示。
图7-17 运行结果
这样咱们就成功获取了京东首页渲染完成后的页面截图,详细的参数设置能够参考官网文档splash.readthedocs.io/en/stable/a…。
此接口和render.png相似,不过它返回的是JPEG格式的图片二进制数据。
另外,此接口比render.png多了参数quality
,它用来设置图片质量。
此接口用于获取页面加载的HAR数据,示例以下:
1
|
curl http://localhost:8050/render.har?url=https://www.jd.com&wait=5
|
它的返回结果(如图7-18所示)很是多,是一个JSON格式的数据,其中包含页面加载过程当中的HAR数据。
图7-18 运行结果
此接口包含了前面接口的全部功能,返回结果是JSON格式,示例以下:
1
|
curl http://localhost:8050/render.json?url=https://httpbin.org
|
结果以下:
1
|
{"title": "httpbin(1): HTTP Client Testing Service", "url": "https://httpbin.org/", "requestedUrl": "https://httpbin.org/", "geometry": [0, 0, 1024, 768]}
|
能够看到,这里以JSON形式返回了相应的请求数据。
咱们能够经过传入不一样参数控制其返回结果。好比,传入html=1
,返回结果即会增长源代码数据;传入png=1
,返回结果即会增长页面PNG截图数据;传入har=1
,则会得到页面HAR数据。例如:
1
|
curl http://localhost:8050/render.json?url=https://httpbin.org&html=1&har=1
|
这样返回的JSON结果会包含网页源代码和HAR数据。
此外还有更多参数设置,具体能够参考官方文档:splash.readthedocs.io/en/stable/a…。
此接口才是最为强大的接口。前面说了不少Splash Lua脚本的操做,用此接口即可实现与Lua脚本的对接。
前面的render.html和render.png等接口对于通常的JavaScript渲染页面是足够了,可是若是要实现一些交互操做的话,它们仍是无能为力,这里就须要使用execute接口了。
咱们先实现一个最简单的脚本,直接返回数据:
1
2
3
|
function main(splash)
return 'hello'
end
|
而后将此脚本转化为URL编码后的字符串,拼接到execute接口后面,示例以下:
1
|
curl http://localhost:8050/execute?lua_source=function+main%28splash%29%0D%0A++return+%27hello%27%0D%0Aend
|
运行结果以下:
1
|
hello
|
这里咱们经过lua_source
参数传递了转码后的Lua脚本,经过execute接口获取了最终脚本的执行结果。
这里咱们更加关心的确定是如何用Python来实现,上例用Python实现的话,代码以下:
1
2
3
4
5
6
7
8
9
10
11
12
|
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)
|
运行结果以下:
1
|
hello
|
这里咱们用Python中的三引号将Lua脚本包括起来,而后用urllib.parse模块里的quote()
方法将脚本进行URL转码,随后构造了Splash请求URL,将其做为lua_source
参数传递,这样运行结果就会显示Lua脚本执行后的结果。
咱们再经过实例看一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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)
|
运行结果以下:
1
|
{"url": "http://httpbin.org/get", "status": 200, "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\": \"60.207.237.85\", \n \"url\": \"http://httpbin.org/get\"\n}\n"}
|
能够看到,返回结果是JSON形式,咱们成功获取了请求的URL、状态码和网页源代码。
如此一来,咱们以前所说的Lua脚本都可以用此方式与Python进行对接,全部网页的动态渲染、模拟点击、表单提交、页面滑动、延时等待后的一些结果都可以自由控制,获取页面源码和截图也都不在话下。
到如今为止,咱们能够用Python和Splash实现JavaScript渲染的页面的抓取了。除了Selenium,本节所说的Splash一样能够作到很是强大的渲染功能,同时它也不须要浏览器便可渲染,使用很是方便。
本资源首发于崔庆才的我的博客静觅: Python3网络爬虫开发实战教程 | 静觅
如想了解更多爬虫资讯,请关注个人我的微信公众号:进击的Coder
weixin.qq.com/r/5zsjOyvEZ… (二维码自动识别)