Python3使用Splash进行爬虫操作,有哪些关键知识点需要总结?

2026-05-22 03:551阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计5618个文字,预计阅读时间需要23分钟。

Python3使用Splash进行爬虫操作,有哪些关键知识点需要总结?

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

1. 功能介绍利用Splash,我们可以:- 实现动态渲染页面的抓取

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

1. 功能介绍

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

·异步方式处理多个网页渲染过程;

·获取渲染后的页面的源代码或截图;

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

·可执行特定的JavaScript脚本;

·可通过Lua脚本来控制页面渲染过程;

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

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

2. 准备工作

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

3. 实例引入

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

图7-6 Web页面

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

图7-7 运行结果

可以看到,网页的返回结果呈现了渲染截图、HAR加载统计数据、网页的源代码。

通过HAR的结果可以看到,Splash执行了整个网页的渲染过程,包括CSS、JavaScript的加载等过程,呈现的页面和我们在浏览器中得到的结果完全一致。

那么,这个过程由什么来控制呢?重新返回首页,可以看到实际上是有一段脚本,内容如下:

functionmain(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的用法。

4. Splash Lua脚本

Splash可以通过Lua脚本执行一系列渲染操作,这样我们就可以用Splash来模拟类似Chrome、PhantomJS的操作了。

首先,我们来了解一下Splash Lua脚本的入口和执行方式。

入口及返回值

首先,来看一个基本实例:

functionmain(splash,args) splash:go("www.baidu.com") splash:wait(0.5) localtitle=splash:evaljs("document.title") return{title=title} end

我们将代码粘贴到刚才打开的localhost:8050/的代码编辑区域,然后点击Render me!按钮来测试一下。

我们看到它返回了网页的标题,如图7-8所示。这里我们通过evaljs()方法传入JavaScript脚本,而document.title的执行结果就是返回网页标题,执行完毕后将其赋值给一个title变量,随后将其返回。

图7-8 运行结果

注意,我们在这里定义的方法名称叫作main()。这个名称必须是固定的,Splash会默认调用这个方法。

该方法的返回值既可以是字典形式,也可以是字符串形式,最后都会转化为Splash HTTP Response,例如:

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

返回了一个字典形式的内容。例如:

functionmain(splash) return'hello' end

返回了一个字符串形式的内容。

异步处理

Splash支持异步处理,但是这里并没有显式指明回调方法,其回调的跳转是在Splash内部完成的。示例如下:

functionmain(splash,args) localexample_urls={"www.baidu.com","www.taobao.com","www.zhihu.com"} localurls=args.urlsorexample_urls localresults={} forindex,urlinipairs(urls)do localok,reason=splash:go(""..url) ifokthen splash:wait(2) results[url]=splash:png() end end returnresults end

运行结果是3个站点的截图,如图7-9所示。

图7-9 运行结果

在脚本内调用的wait()方法类似于Python中的sleep(),其参数为等待的秒数。当Splash执行到此方法时,它会转而去处理其他任务,然后在指定的时间过后再回来继续处理。

这里值得注意的是,Lua脚本中的字符串拼接和Python不同,它使用的是..操作符,而不是+。如果有必要,可以简单了解一下Lua脚本的语法,详见www.runoob.com/lua/lua-basic-syntax.html。

另外,这里做了加载时的异常检测。go()方法会返回加载页面的结果状态,如果页面出现4xx或5xx状态码,ok变量就为空,就不会返回加载后的图片。

5. Splash对象属性

我们注意到,前面例子中main()方法的第一个参数是splash,这个对象非常重要,它类似于Selenium中的WebDriver对象,我们可以调用它的一些属性和方法来控制加载过程。接下来,先看下它的属性。

args

该属性可以获取加载时配置的参数,比如URL,如果为GET请求,它还可以获取GET请求参数;如果为POST请求,它可以获取表单提交的数据。Splash也支持使用第二个参数直接作为args,例如:

functionmain(splash,args) localurl=args.url end

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

functionmain(splash) localurl=splash.args.url end

js_enabled

这个属性是Splash的JavaScript执行开关,可以将其配置为true或false来控制是否执行JavaScript代码,默认为true。例如,这里禁止执行JavaScript代码:

functionmain(splash,args) splash:go("www.baidu.com") splash.js_enabled=false localtitle=splash:evaljs("document.title") return{title=title} end

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

{ "error":400, "type":"ScriptError", "info":{ "type":"JS_ERROR", "js_error_message":null, "source":"[string\"functionmain(splash,args)\r...\"]", "message":"[string\"functionmain(splash,args)\r...\"]:4:unknownJSerror:None", "line_number":4, "error":"unknownJSerror:None", "splash_method":"evaljs" }, "description":"ErrorhappenedwhileexecutingLuascript" }

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

resource_timeout

此属性可以设置加载的超时时间,单位是秒。如果设置为0或nil(类似Python中的None),代表不检测超时。示例如下:

functionmain(splash) splash.resource_timeout=0.1 assert(splash:go('www.taobao.com')) returnsplash:png() end

例如,这里将超时时间设置为0.1秒。如果在0.1秒之内没有得到响应,就会抛出异常,错误如下:

{ "error":400, "type":"ScriptError", "info":{ "error":"network5", "type":"LUA_ERROR", "line_number":3, "source":"[string\"functionmain(splash)\r...\"]", "message":"Luaerror:[string\"functionmain(splash)\r...\"]:3:network5" }, "description":"ErrorhappenedwhileexecutingLuascript" }

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

images_enabled

此属性可以设置图片是否加载,默认情况下是加载的。禁用该属性后,可以节省网络流量并提高网页加载速度。但是需要注意的是,禁用图片加载可能会影响JavaScript渲染。因为禁用图片之后,它的外层DOM节点的高度会受影响,进而影响DOM节点的位置。因此,如果JavaScript对图片节点有操作的话,其执行就会受到影响。

另外值得注意的是,Splash使用了缓存。如果一开始加载出来了网页图片,然后禁用了图片加载,再重新加载页面,之前加载好的图片可能还会显示出来,这时直接重启Splash即可。

禁用图片加载的示例如下:

functionmain(splash,args) splash.images_enabled=false assert(splash:go('www.jd.com')) return{png=splash:png()} end

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

plugins_enabled

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

splash.plugins_enabled=true/false

scroll_position

通过设置此属性,我们可以控制页面上下或左右滚动。这是一个比较常用的属性,示例如下:

functionmain(splash,args) assert(splash:go('www.taobao.com')) splash.scroll_position={y=400} return{png=splash:png()} end

这样我们就可以控制页面向下滚动400像素值,结果如图7-10所示。

图7-10 运行结果

如果要让页面左右滚动,可以传入x参数,代码如下:

splash.scroll_position={x=100,y=200}

6. Splash对象的方法

除了前面介绍的属性外,Splash对象还有如下方法。

go()

该方法用来请求某个链接,而且它可以模拟GET和POST请求,同时支持传入请求头、表单等数据,其用法如下:

ok,reason=splash:go{url,baseurl=nil,headers=nil,www.taobao.com") splash:wait(2) return{html=splash:html()} end

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

jsfunc()

此方法可以直接调用JavaScript定义的方法,但是所调用的方法需要用双中括号包围,这相当于实现了JavaScript方法到Lua脚本的转换。示例如下:

functionmain(splash,args) localget_div_count=splash:jsfunc([[ function(){ varbody=document.body; vardivs=body.getElementsByTagName('div'); returndivs.length; } ]]) splash:go("www.baidu.com") return("Thereare%sDIVs"):format( get_div_count()) end

运行结果如下:

Thereare21DIVs

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

关于JavaScript到Lua脚本的更多转换细节,可以参考官方文档:splash.readthedocs.io/en/stable/scripting-ref.html#splash-jsfunc。

evaljs()

此方法可以执行JavaScript代码并返回最后一条JavaScript语句的返回结果,使用方法如下:

result=splash:evaljs(js)

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

localtitle=splash:evaljs("document.title")

runjs()

此方法可以执行JavaScript代码,它与evaljs()的功能类似,但是更偏向于执行某些动作或声明某些方法。例如:

functionmain(splash,args) splash:go("www.baidu.com") splash:runjs("foo=function(){return'bar'}") localresult=splash:evaljs("foo()") returnresult end

这里我们用runjs()先声明了一个JavaScript定义的方法,然后通过evaljs()来调用得到的结果。

运行结果如下:

bar

autoload()

此方法可以设置每个页面访问时自动加载的对象,使用方法如下:

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

参数说明如下。

source_or_url:JavaScript代码或者JavaScript库链接。

source:JavaScript代码。

url:JavaScript库链接

但是此方法只负责加载JavaScript代码或库,不执行任何操作。如果要执行操作,可以调用evaljs()或runjs()方法。示例如下:

functionmain(splash,args) splash:autoload([[ functionget_document_title(){ returndocument.title; } ]]) splash:go("www.baidu.com") returnsplash:evaljs("get_document_title()") end

这里我们调用autoload()方法声明了一个JavaScript方法,然后通过evaljs()方法来执行此JavaScript方法。

运行结果如下:

百度一下,你就知道

另外,我们也可以使用autoload()方法加载某些方法库,如jQuery,示例如下:

functionmain(splash,args) assert(splash:autoload("code.jquery.com/jquery-2.1.3.min.js")) assert(splash:go("www.taobao.com")) localversion=splash:evaljs("$.fn.jquery") return'JQueryversion:'..version end

运行结果如下:

JQueryversion:2.1.3

call_later()

此方法可以通过设置定时任务和延迟时间来实现任务延时执行,并且可以在执行前通过cancel()方法重新执行定时任务。示例如下:

functionmain(splash,args) localsnapshots={} localtimer=splash:call_later(function() snapshots["a"]=splash:png() splash:wait(1.0) snapshots["b"]=splash:png() end,0.2) splash:go("www.taobao.com") splash:wait(3.0) returnsnapshots end

这里我们设置了一个定时任务,0.2秒的时候获取网页截图,然后等待1秒,1.2秒时再次获取网页截图,访问的页面是淘宝,最后将截图结果返回。运行结果如图7-11所示。

图7-11 运行结果

可以发现,第一次截图时网页还没有加载出来,截图为空,第二次网页便加载成功了。

www.taobao.com") returnsplash:png() end

jpeg()

此方法用来获取JPEG格式的网页截图,示例如下:

functionmain(splash,args) splash:go("www.taobao.com") returnsplash:jpeg() end

har()

此方法用来获取页面加载过程描述,示例如下:

functionmain(splash,args) splash:go("www.baidu.com") returnsplash:har() end

运行结果如图7-13所示,其中显示了页面加载过程中每个请求记录的详情。

图7-13 运行结果

url()

此方法可以获取当前正在访问的URL,示例如下:

functionmain(splash,args) splash:go("www.baidu.com") returnsplash:url() end

运行结果如下:

www.baidu.com/

get_cookies()

此方法可以获取当前页面的Cookies,示例如下:

functionmain(splash,args) splash:go("www.baidu.com") returnsplash:get_cookies() end

运行结果如下:

SplashResponse:Array[2] 0:Object domain:".baidu.com" expires:"2085-08-21T20:13:23Z" example.com"} splash:go("example.com/") returnsplash:html() end

clear_cookies()

此方法可以清除所有的Cookies,示例如下:

functionmain(splash) splash:go("www.baidu.com/") splash:clear_cookies() returnsplash:get_cookies() end

这里我们清除了所有的Cookies,然后调用get_cookies()将结果返回。

运行结果如下:

SplashResponse:Array[0]

可以看到,Cookies被全部清空,没有任何结果。

get_viewport_size()

此方法可以获取当前浏览器页面的大小,即宽高,示例如下:

functionmain(splash) splash:go("www.baidu.com/") returnsplash:get_viewport_size() end

运行结果如下:

SplashResponse:Array[2] 0:1024 1:768

set_viewport_size()

此方法可以设置当前浏览器页面的大小,即宽高,用法如下:

splash:set_viewport_size(width,height)

例如,这里访问一个宽度自适应的页面:

functionmain(splash) splash:set_viewport_size(400,700) assert(splash:go("cuiqingcai.com")) returnsplash:png() end

运行结果如图7-14所示。

图7-14 运行结果

set_viewport_full()

此方法可以设置浏览器全屏显示,示例如下:

functionmain(splash) splash:set_viewport_full() assert(splash:go("cuiqingcai.com")) returnsplash:png() end

此方法可以设置浏览器的User-Agent,示例如下:

functionmain(splash) splash:set_user_agent('Splash') splash:go("www.baidu.com/") input=splash:select("#kw") input:send_text('Splash') splash:wait(3) returnsplash:png() end

这里我们首先访问了百度,然后选中了搜索框,随后调用了send_text()方法填写了文本,然后返回网页截图。

结果如图7-15所示,可以看到,我们成功填写了输入框。

图7-15 运行结果

select_all()

此方法可以选中所有符合条件的节点,其参数是CSS选择器。示例如下:

functionmain(splash) localtreat=require('treat') assert(splash:go("quotes.toscrape.com/")) assert(splash:wait(0.5)) localtexts=splash:select_all('.quote.text') localresults={} forindex,textinipairs(texts)do results[index]=text.node.innerHTML end returntreat.as_array(results) end

这里我们通过CSS选择器选中了节点的正文内容,随后遍历了所有节点,将其中的文本获取下来。

运行结果如下:

SplashResponse:Array[10] 0:"“Theworldaswehavecreateditisaprocessofourthinking.Itcannotbechangedwithoutchangingour thinking.”" 1:"“Itisourchoices,Harry,thatshowwhatwetrulyare,farmorethanourabilities.”" 2:“Thereareonlytwowaystoliveyourlife.Oneisasthoughnothingisamiracle.Theotherisasthough everythingisamiracle.” 3:"“Theperson,beitgentlemanorlady,whohasnotpleasureinagoodnovel,mustbeintolerablystupid.”" 4:"“Imperfectionisbeauty,madnessisgeniusandit'sbettertobeabsolutelyridiculousthanabsolutelyboring.”" 5:"“Trynottobecomeamanofsuccess.Ratherbecomeamanofvalue.”" 6:"“Itisbettertobehatedforwhatyouarethantobelovedforwhatyouarenot.”" 7:"“Ihavenotfailed.I'vejustfound10,000waysthatwon'twork.”" 8:"“Awomanislikeateabag;youneverknowhowstrongitisuntilit'sinhotwater.”" 9:"“Adaywithoutsunshineislike,youknow,night.”"

可以发现,我们成功地将10个节点的正文内容获取了下来。

Python3使用Splash进行爬虫操作,有哪些关键知识点需要总结?

mouse_click()

此方法可以模拟鼠标点击操作,传入的参数为坐标值x和y。此外,也可以直接选中某个节点,然后调用此方法,示例如下:

functionmain(splash) splash:go("www.baidu.com/") input=splash:select("#kw") input:send_text('Splash') submit=splash:select('#su') submit:mouse_click() splash:wait(3) returnsplash:png() end

这里我们首先选中页面的输入框,输入了文本,然后选中“提交”按钮,调用了mouse_click()方法提交查询,然后页面等待三秒,返回截图,结果如图7-16所示。

图7-16 运行结果

可以看到,这里我们成功获取了查询后的页面内容,模拟了百度搜索操作。

前面介绍了Splash的常用API操作,还有一些API在这不再一一介绍,更加详细和权威的说明可以参见官方文档splash.readthedocs.io/en/stable/scripting-ref.html,此页面介绍了Splash对象的所有API操作。另外,还有针对页面元素的API操作,链接为splash.readthedocs.io/en/stable/scripting-element-object.html。

7. Splash API调用

前面说明了Splash Lua脚本的用法,但这些脚本是在Splash页面中测试运行的,如何才能利用Splash渲染页面呢?怎样才能和Python程序结合使用并抓取JavaScript渲染的页面呢?

其实Splash给我们提供了一些HTTP API接口,我们只需要请求这些接口并传递相应的参数即可,下面简要介绍这些接口。

render.html

此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称,例如localhost:8050/render.html。可以用curl来测试一下:

curllocalhost:8050/render.html?url=www.baidu.com

我们给此接口传递了一个url参数来指定渲染的URL,返回结果即页面渲染后的源代码。

如果用Python实现的话,代码如下:

importrequests url='localhost:8050/render.html?url=www.baidu.com' response=requests.get(url) print(response.text)

这样就可以成功输出百度页面渲染后的源代码了。

另外,此接口还可以指定其他参数,比如通过wait指定等待秒数。如果要确保页面完全加载出来,可以增加等待时间,例如:

importrequests url='localhost:8050/render.html?url=www.taobao.com&wait=5' response=requests.get(url) print(response.text)

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

另外,此接口还支持代理设置、图片加载设置、Headers设置、请求方法设置,具体的用法可以参见官方文档splash.readthedocs.io/en/stable/api.html#render-html。

render.png

此接口可以获取网页截图,其参数比render.html多了几个,比如通过width和height来控制宽高,它返回的是PNG格式的图片二进制数据。示例如下:

curllocalhost:8050/render.png?url=www.taobao.com&wait=5&width=1000&height=700

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

如果用Python实现,可以将返回的二进制数据保存为PNG格式的图片,具体如下:

importrequests url='localhost:8050/render.png?url=www.jd.com&wait=5&width=1000&height=700' response=requests.get(url) withopen('taobao.png','wb')asf: f.write(response.content)

得到的图片如图7-17所示。

图7-17 运行结果

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

render.jpeg

此接口和render.png类似,不过它返回的是JPEG格式的图片二进制数据。

另外,此接口比render.png多了参数quality,它用来设置图片质量。

render.har

此接口用于获取页面加载的HAR数据,示例如下:

curllocalhost:8050/render.har?url=www.jd.com&wait=5

它的返回结果(如图7-18所示)非常多,是一个JSON格式的数据,其中包含页面加载过程中的HAR数据。

图7-18 运行结果

render.json

此接口包含了前面接口的所有功能,返回结果是JSON格式,示例如下:

curllocalhost:8050/render.json?url=localhost:8050/render.json?url=splash.readthedocs.io/en/stable/api.html#render-json。

execute

此接口才是最为强大的接口。前面说了很多Splash Lua脚本的操作,用此接口便可实现与Lua脚本的对接。

前面的render.html和render.png等接口对于一般的JavaScript渲染页面是足够了,但是如果要实现一些交互操作的话,它们还是无能为力,这里就需要使用execute接口了。

我们先实现一个最简单的脚本,直接返回数据:

functionmain(splash) return'hello' end

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

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

运行结果如下:

hello

这里我们通过lua_source参数传递了转码后的Lua脚本,通过execute接口获取了最终脚本的执行结果。

这里我们更加关心的肯定是如何用Python来实现,上例用Python实现的话,代码如下:

importrequests fromurllib.parseimportquote lua=''' functionmain(splash) return'hello' end ''' url='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脚本执行后的结果。

我们再通过实例看一下:

importrequests fromurllib.parseimportquote lua=''' functionmain(splash,args) localtreat=require("treat") localresponse=splash:localhost:8050/execute?lua_source='+quote(lua) response=requests.get(url) print(response.text)

运行结果如下:

{"url":"httpbin.org/get\"\n}\n"}

可以看到,返回结果是JSON形式,我们成功获取了请求的URL、状态码和网页源代码。

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

到现在为止,我们可以用Python和Splash实现JavaScript渲染的页面的抓取了。除了Selenium,本节所说的Splash同样可以做到非常强大的渲染功能,同时它也不需要浏览器即可渲染,使用非常方便。

到此这篇关于Python3爬虫中Splash的知识总结的文章就介绍到这了,更多相关Python3中Splash的基础知识内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

本文共计5618个文字,预计阅读时间需要23分钟。

Python3使用Splash进行爬虫操作,有哪些关键知识点需要总结?

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

1. 功能介绍利用Splash,我们可以:- 实现动态渲染页面的抓取

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

1. 功能介绍

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

·异步方式处理多个网页渲染过程;

·获取渲染后的页面的源代码或截图;

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

·可执行特定的JavaScript脚本;

·可通过Lua脚本来控制页面渲染过程;

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

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

2. 准备工作

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

3. 实例引入

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

图7-6 Web页面

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

图7-7 运行结果

可以看到,网页的返回结果呈现了渲染截图、HAR加载统计数据、网页的源代码。

通过HAR的结果可以看到,Splash执行了整个网页的渲染过程,包括CSS、JavaScript的加载等过程,呈现的页面和我们在浏览器中得到的结果完全一致。

那么,这个过程由什么来控制呢?重新返回首页,可以看到实际上是有一段脚本,内容如下:

functionmain(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的用法。

4. Splash Lua脚本

Splash可以通过Lua脚本执行一系列渲染操作,这样我们就可以用Splash来模拟类似Chrome、PhantomJS的操作了。

首先,我们来了解一下Splash Lua脚本的入口和执行方式。

入口及返回值

首先,来看一个基本实例:

functionmain(splash,args) splash:go("www.baidu.com") splash:wait(0.5) localtitle=splash:evaljs("document.title") return{title=title} end

我们将代码粘贴到刚才打开的localhost:8050/的代码编辑区域,然后点击Render me!按钮来测试一下。

我们看到它返回了网页的标题,如图7-8所示。这里我们通过evaljs()方法传入JavaScript脚本,而document.title的执行结果就是返回网页标题,执行完毕后将其赋值给一个title变量,随后将其返回。

图7-8 运行结果

注意,我们在这里定义的方法名称叫作main()。这个名称必须是固定的,Splash会默认调用这个方法。

该方法的返回值既可以是字典形式,也可以是字符串形式,最后都会转化为Splash HTTP Response,例如:

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

返回了一个字典形式的内容。例如:

functionmain(splash) return'hello' end

返回了一个字符串形式的内容。

异步处理

Splash支持异步处理,但是这里并没有显式指明回调方法,其回调的跳转是在Splash内部完成的。示例如下:

functionmain(splash,args) localexample_urls={"www.baidu.com","www.taobao.com","www.zhihu.com"} localurls=args.urlsorexample_urls localresults={} forindex,urlinipairs(urls)do localok,reason=splash:go(""..url) ifokthen splash:wait(2) results[url]=splash:png() end end returnresults end

运行结果是3个站点的截图,如图7-9所示。

图7-9 运行结果

在脚本内调用的wait()方法类似于Python中的sleep(),其参数为等待的秒数。当Splash执行到此方法时,它会转而去处理其他任务,然后在指定的时间过后再回来继续处理。

这里值得注意的是,Lua脚本中的字符串拼接和Python不同,它使用的是..操作符,而不是+。如果有必要,可以简单了解一下Lua脚本的语法,详见www.runoob.com/lua/lua-basic-syntax.html。

另外,这里做了加载时的异常检测。go()方法会返回加载页面的结果状态,如果页面出现4xx或5xx状态码,ok变量就为空,就不会返回加载后的图片。

5. Splash对象属性

我们注意到,前面例子中main()方法的第一个参数是splash,这个对象非常重要,它类似于Selenium中的WebDriver对象,我们可以调用它的一些属性和方法来控制加载过程。接下来,先看下它的属性。

args

该属性可以获取加载时配置的参数,比如URL,如果为GET请求,它还可以获取GET请求参数;如果为POST请求,它可以获取表单提交的数据。Splash也支持使用第二个参数直接作为args,例如:

functionmain(splash,args) localurl=args.url end

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

functionmain(splash) localurl=splash.args.url end

js_enabled

这个属性是Splash的JavaScript执行开关,可以将其配置为true或false来控制是否执行JavaScript代码,默认为true。例如,这里禁止执行JavaScript代码:

functionmain(splash,args) splash:go("www.baidu.com") splash.js_enabled=false localtitle=splash:evaljs("document.title") return{title=title} end

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

{ "error":400, "type":"ScriptError", "info":{ "type":"JS_ERROR", "js_error_message":null, "source":"[string\"functionmain(splash,args)\r...\"]", "message":"[string\"functionmain(splash,args)\r...\"]:4:unknownJSerror:None", "line_number":4, "error":"unknownJSerror:None", "splash_method":"evaljs" }, "description":"ErrorhappenedwhileexecutingLuascript" }

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

resource_timeout

此属性可以设置加载的超时时间,单位是秒。如果设置为0或nil(类似Python中的None),代表不检测超时。示例如下:

functionmain(splash) splash.resource_timeout=0.1 assert(splash:go('www.taobao.com')) returnsplash:png() end

例如,这里将超时时间设置为0.1秒。如果在0.1秒之内没有得到响应,就会抛出异常,错误如下:

{ "error":400, "type":"ScriptError", "info":{ "error":"network5", "type":"LUA_ERROR", "line_number":3, "source":"[string\"functionmain(splash)\r...\"]", "message":"Luaerror:[string\"functionmain(splash)\r...\"]:3:network5" }, "description":"ErrorhappenedwhileexecutingLuascript" }

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

images_enabled

此属性可以设置图片是否加载,默认情况下是加载的。禁用该属性后,可以节省网络流量并提高网页加载速度。但是需要注意的是,禁用图片加载可能会影响JavaScript渲染。因为禁用图片之后,它的外层DOM节点的高度会受影响,进而影响DOM节点的位置。因此,如果JavaScript对图片节点有操作的话,其执行就会受到影响。

另外值得注意的是,Splash使用了缓存。如果一开始加载出来了网页图片,然后禁用了图片加载,再重新加载页面,之前加载好的图片可能还会显示出来,这时直接重启Splash即可。

禁用图片加载的示例如下:

functionmain(splash,args) splash.images_enabled=false assert(splash:go('www.jd.com')) return{png=splash:png()} end

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

plugins_enabled

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

splash.plugins_enabled=true/false

scroll_position

通过设置此属性,我们可以控制页面上下或左右滚动。这是一个比较常用的属性,示例如下:

functionmain(splash,args) assert(splash:go('www.taobao.com')) splash.scroll_position={y=400} return{png=splash:png()} end

这样我们就可以控制页面向下滚动400像素值,结果如图7-10所示。

图7-10 运行结果

如果要让页面左右滚动,可以传入x参数,代码如下:

splash.scroll_position={x=100,y=200}

6. Splash对象的方法

除了前面介绍的属性外,Splash对象还有如下方法。

go()

该方法用来请求某个链接,而且它可以模拟GET和POST请求,同时支持传入请求头、表单等数据,其用法如下:

ok,reason=splash:go{url,baseurl=nil,headers=nil,www.taobao.com") splash:wait(2) return{html=splash:html()} end

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

jsfunc()

此方法可以直接调用JavaScript定义的方法,但是所调用的方法需要用双中括号包围,这相当于实现了JavaScript方法到Lua脚本的转换。示例如下:

functionmain(splash,args) localget_div_count=splash:jsfunc([[ function(){ varbody=document.body; vardivs=body.getElementsByTagName('div'); returndivs.length; } ]]) splash:go("www.baidu.com") return("Thereare%sDIVs"):format( get_div_count()) end

运行结果如下:

Thereare21DIVs

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

关于JavaScript到Lua脚本的更多转换细节,可以参考官方文档:splash.readthedocs.io/en/stable/scripting-ref.html#splash-jsfunc。

evaljs()

此方法可以执行JavaScript代码并返回最后一条JavaScript语句的返回结果,使用方法如下:

result=splash:evaljs(js)

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

localtitle=splash:evaljs("document.title")

runjs()

此方法可以执行JavaScript代码,它与evaljs()的功能类似,但是更偏向于执行某些动作或声明某些方法。例如:

functionmain(splash,args) splash:go("www.baidu.com") splash:runjs("foo=function(){return'bar'}") localresult=splash:evaljs("foo()") returnresult end

这里我们用runjs()先声明了一个JavaScript定义的方法,然后通过evaljs()来调用得到的结果。

运行结果如下:

bar

autoload()

此方法可以设置每个页面访问时自动加载的对象,使用方法如下:

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

参数说明如下。

source_or_url:JavaScript代码或者JavaScript库链接。

source:JavaScript代码。

url:JavaScript库链接

但是此方法只负责加载JavaScript代码或库,不执行任何操作。如果要执行操作,可以调用evaljs()或runjs()方法。示例如下:

functionmain(splash,args) splash:autoload([[ functionget_document_title(){ returndocument.title; } ]]) splash:go("www.baidu.com") returnsplash:evaljs("get_document_title()") end

这里我们调用autoload()方法声明了一个JavaScript方法,然后通过evaljs()方法来执行此JavaScript方法。

运行结果如下:

百度一下,你就知道

另外,我们也可以使用autoload()方法加载某些方法库,如jQuery,示例如下:

functionmain(splash,args) assert(splash:autoload("code.jquery.com/jquery-2.1.3.min.js")) assert(splash:go("www.taobao.com")) localversion=splash:evaljs("$.fn.jquery") return'JQueryversion:'..version end

运行结果如下:

JQueryversion:2.1.3

call_later()

此方法可以通过设置定时任务和延迟时间来实现任务延时执行,并且可以在执行前通过cancel()方法重新执行定时任务。示例如下:

functionmain(splash,args) localsnapshots={} localtimer=splash:call_later(function() snapshots["a"]=splash:png() splash:wait(1.0) snapshots["b"]=splash:png() end,0.2) splash:go("www.taobao.com") splash:wait(3.0) returnsnapshots end

这里我们设置了一个定时任务,0.2秒的时候获取网页截图,然后等待1秒,1.2秒时再次获取网页截图,访问的页面是淘宝,最后将截图结果返回。运行结果如图7-11所示。

图7-11 运行结果

可以发现,第一次截图时网页还没有加载出来,截图为空,第二次网页便加载成功了。

www.taobao.com") returnsplash:png() end

jpeg()

此方法用来获取JPEG格式的网页截图,示例如下:

functionmain(splash,args) splash:go("www.taobao.com") returnsplash:jpeg() end

har()

此方法用来获取页面加载过程描述,示例如下:

functionmain(splash,args) splash:go("www.baidu.com") returnsplash:har() end

运行结果如图7-13所示,其中显示了页面加载过程中每个请求记录的详情。

图7-13 运行结果

url()

此方法可以获取当前正在访问的URL,示例如下:

functionmain(splash,args) splash:go("www.baidu.com") returnsplash:url() end

运行结果如下:

www.baidu.com/

get_cookies()

此方法可以获取当前页面的Cookies,示例如下:

functionmain(splash,args) splash:go("www.baidu.com") returnsplash:get_cookies() end

运行结果如下:

SplashResponse:Array[2] 0:Object domain:".baidu.com" expires:"2085-08-21T20:13:23Z" example.com"} splash:go("example.com/") returnsplash:html() end

clear_cookies()

此方法可以清除所有的Cookies,示例如下:

functionmain(splash) splash:go("www.baidu.com/") splash:clear_cookies() returnsplash:get_cookies() end

这里我们清除了所有的Cookies,然后调用get_cookies()将结果返回。

运行结果如下:

SplashResponse:Array[0]

可以看到,Cookies被全部清空,没有任何结果。

get_viewport_size()

此方法可以获取当前浏览器页面的大小,即宽高,示例如下:

functionmain(splash) splash:go("www.baidu.com/") returnsplash:get_viewport_size() end

运行结果如下:

SplashResponse:Array[2] 0:1024 1:768

set_viewport_size()

此方法可以设置当前浏览器页面的大小,即宽高,用法如下:

splash:set_viewport_size(width,height)

例如,这里访问一个宽度自适应的页面:

functionmain(splash) splash:set_viewport_size(400,700) assert(splash:go("cuiqingcai.com")) returnsplash:png() end

运行结果如图7-14所示。

图7-14 运行结果

set_viewport_full()

此方法可以设置浏览器全屏显示,示例如下:

functionmain(splash) splash:set_viewport_full() assert(splash:go("cuiqingcai.com")) returnsplash:png() end

此方法可以设置浏览器的User-Agent,示例如下:

functionmain(splash) splash:set_user_agent('Splash') splash:go("www.baidu.com/") input=splash:select("#kw") input:send_text('Splash') splash:wait(3) returnsplash:png() end

这里我们首先访问了百度,然后选中了搜索框,随后调用了send_text()方法填写了文本,然后返回网页截图。

结果如图7-15所示,可以看到,我们成功填写了输入框。

图7-15 运行结果

select_all()

此方法可以选中所有符合条件的节点,其参数是CSS选择器。示例如下:

functionmain(splash) localtreat=require('treat') assert(splash:go("quotes.toscrape.com/")) assert(splash:wait(0.5)) localtexts=splash:select_all('.quote.text') localresults={} forindex,textinipairs(texts)do results[index]=text.node.innerHTML end returntreat.as_array(results) end

这里我们通过CSS选择器选中了节点的正文内容,随后遍历了所有节点,将其中的文本获取下来。

运行结果如下:

SplashResponse:Array[10] 0:"“Theworldaswehavecreateditisaprocessofourthinking.Itcannotbechangedwithoutchangingour thinking.”" 1:"“Itisourchoices,Harry,thatshowwhatwetrulyare,farmorethanourabilities.”" 2:“Thereareonlytwowaystoliveyourlife.Oneisasthoughnothingisamiracle.Theotherisasthough everythingisamiracle.” 3:"“Theperson,beitgentlemanorlady,whohasnotpleasureinagoodnovel,mustbeintolerablystupid.”" 4:"“Imperfectionisbeauty,madnessisgeniusandit'sbettertobeabsolutelyridiculousthanabsolutelyboring.”" 5:"“Trynottobecomeamanofsuccess.Ratherbecomeamanofvalue.”" 6:"“Itisbettertobehatedforwhatyouarethantobelovedforwhatyouarenot.”" 7:"“Ihavenotfailed.I'vejustfound10,000waysthatwon'twork.”" 8:"“Awomanislikeateabag;youneverknowhowstrongitisuntilit'sinhotwater.”" 9:"“Adaywithoutsunshineislike,youknow,night.”"

可以发现,我们成功地将10个节点的正文内容获取了下来。

Python3使用Splash进行爬虫操作,有哪些关键知识点需要总结?

mouse_click()

此方法可以模拟鼠标点击操作,传入的参数为坐标值x和y。此外,也可以直接选中某个节点,然后调用此方法,示例如下:

functionmain(splash) splash:go("www.baidu.com/") input=splash:select("#kw") input:send_text('Splash') submit=splash:select('#su') submit:mouse_click() splash:wait(3) returnsplash:png() end

这里我们首先选中页面的输入框,输入了文本,然后选中“提交”按钮,调用了mouse_click()方法提交查询,然后页面等待三秒,返回截图,结果如图7-16所示。

图7-16 运行结果

可以看到,这里我们成功获取了查询后的页面内容,模拟了百度搜索操作。

前面介绍了Splash的常用API操作,还有一些API在这不再一一介绍,更加详细和权威的说明可以参见官方文档splash.readthedocs.io/en/stable/scripting-ref.html,此页面介绍了Splash对象的所有API操作。另外,还有针对页面元素的API操作,链接为splash.readthedocs.io/en/stable/scripting-element-object.html。

7. Splash API调用

前面说明了Splash Lua脚本的用法,但这些脚本是在Splash页面中测试运行的,如何才能利用Splash渲染页面呢?怎样才能和Python程序结合使用并抓取JavaScript渲染的页面呢?

其实Splash给我们提供了一些HTTP API接口,我们只需要请求这些接口并传递相应的参数即可,下面简要介绍这些接口。

render.html

此接口用于获取JavaScript渲染的页面的HTML代码,接口地址就是Splash的运行地址加此接口名称,例如localhost:8050/render.html。可以用curl来测试一下:

curllocalhost:8050/render.html?url=www.baidu.com

我们给此接口传递了一个url参数来指定渲染的URL,返回结果即页面渲染后的源代码。

如果用Python实现的话,代码如下:

importrequests url='localhost:8050/render.html?url=www.baidu.com' response=requests.get(url) print(response.text)

这样就可以成功输出百度页面渲染后的源代码了。

另外,此接口还可以指定其他参数,比如通过wait指定等待秒数。如果要确保页面完全加载出来,可以增加等待时间,例如:

importrequests url='localhost:8050/render.html?url=www.taobao.com&wait=5' response=requests.get(url) print(response.text)

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

另外,此接口还支持代理设置、图片加载设置、Headers设置、请求方法设置,具体的用法可以参见官方文档splash.readthedocs.io/en/stable/api.html#render-html。

render.png

此接口可以获取网页截图,其参数比render.html多了几个,比如通过width和height来控制宽高,它返回的是PNG格式的图片二进制数据。示例如下:

curllocalhost:8050/render.png?url=www.taobao.com&wait=5&width=1000&height=700

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

如果用Python实现,可以将返回的二进制数据保存为PNG格式的图片,具体如下:

importrequests url='localhost:8050/render.png?url=www.jd.com&wait=5&width=1000&height=700' response=requests.get(url) withopen('taobao.png','wb')asf: f.write(response.content)

得到的图片如图7-17所示。

图7-17 运行结果

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

render.jpeg

此接口和render.png类似,不过它返回的是JPEG格式的图片二进制数据。

另外,此接口比render.png多了参数quality,它用来设置图片质量。

render.har

此接口用于获取页面加载的HAR数据,示例如下:

curllocalhost:8050/render.har?url=www.jd.com&wait=5

它的返回结果(如图7-18所示)非常多,是一个JSON格式的数据,其中包含页面加载过程中的HAR数据。

图7-18 运行结果

render.json

此接口包含了前面接口的所有功能,返回结果是JSON格式,示例如下:

curllocalhost:8050/render.json?url=localhost:8050/render.json?url=splash.readthedocs.io/en/stable/api.html#render-json。

execute

此接口才是最为强大的接口。前面说了很多Splash Lua脚本的操作,用此接口便可实现与Lua脚本的对接。

前面的render.html和render.png等接口对于一般的JavaScript渲染页面是足够了,但是如果要实现一些交互操作的话,它们还是无能为力,这里就需要使用execute接口了。

我们先实现一个最简单的脚本,直接返回数据:

functionmain(splash) return'hello' end

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

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

运行结果如下:

hello

这里我们通过lua_source参数传递了转码后的Lua脚本,通过execute接口获取了最终脚本的执行结果。

这里我们更加关心的肯定是如何用Python来实现,上例用Python实现的话,代码如下:

importrequests fromurllib.parseimportquote lua=''' functionmain(splash) return'hello' end ''' url='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脚本执行后的结果。

我们再通过实例看一下:

importrequests fromurllib.parseimportquote lua=''' functionmain(splash,args) localtreat=require("treat") localresponse=splash:localhost:8050/execute?lua_source='+quote(lua) response=requests.get(url) print(response.text)

运行结果如下:

{"url":"httpbin.org/get\"\n}\n"}

可以看到,返回结果是JSON形式,我们成功获取了请求的URL、状态码和网页源代码。

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

到现在为止,我们可以用Python和Splash实现JavaScript渲染的页面的抓取了。除了Selenium,本节所说的Splash同样可以做到非常强大的渲染功能,同时它也不需要浏览器即可渲染,使用非常方便。

到此这篇关于Python3爬虫中Splash的知识总结的文章就介绍到这了,更多相关Python3中Splash的基础知识内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!