Splash是通过Lua脚本来控制了页面的加载过程, 加载过程完全模拟浏览器,可以执行某些特定js脚本或者方法
安装
使用pip安装scrapy-splash
pip install scrapy-splash
Scrapy-Splash使用Splash HTTP API,因此您还需要一个Splash实例。通常要安装docker并运行Splash,就像这样就足够了:
docker run -p 8050:8050 scrapinghub / splash
docker run -d -p 8050:8050 scrapinghub/splash # 博主自己使用的, 可以在后台实时运行
点击Splash安装文档以获取更多信息。
组件
1.添加Splash服务器地址到你的Scrapy 工程settings.py里面,如下所示:
SPLASH_URL = 'http://localhost:8050'
2.在你的settings.py文件下DOWNLOADER_MIDDLEWARES里激活Splash middleware并改变HttpCompressionMiddleware优先级
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware':723,
'scrapy_splash.SplashMiddleware':725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware':810,
}
Order 723在默认scrapy设置中的HttpProxyMiddleware(750)之前
HttpCompressionMiddleware优先级应该被改变,以便允许高级响应处理; 详情请参阅
3.在你的settings.py下SPIDER_MIDDLEWARES里添加SplashDeduplicateArgsMiddleware
SPIDER_MIDDLEWARES = {
'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}
这个中间件需要支持cache_args特性;它允许通过不在磁盘请求队列中多次存储重复的Splash参数来节省磁盘空间。如果使用Splash 2.1+,则中间件还可以通过不将这些重复的参数多次发送到Splash服务器来节省网络流量。
4.设置自定义DUPEFILTER_CLASS
DUPEFILTER_CLASS ='scrapy_splash.SplashAwareDupeFilter'
5.如果您使用Scrapy HTTP缓存,则需要自定义缓存存储后端。scrapy-splash提供了以下的一个子类scrapy.contrib.httpcache.FilesystemCacheStorage
HTTPCACHE_STORAGE ='scrapy_splash.SplashAwareFSCacheStorage'
如果您使用其他缓存存储,则需要对其进行子类化并使用其替换所有scrapy.util.request.request_fingerprint调用scrapy_splash.splash_request_fingerprint
注意
步骤(4)和(5)是必需的,因为Scrapy不提供全局覆盖请求指纹计算算法的方法; 这可能会在未来发生变化。
还有一些其他选项可用。在settings.py里, 如果你想改变默认值,将它们放入你的位置:
- SPLASH_COOKIES_DEBUG默认是False。设置为True启用调试中的cookie SplashCookiesMiddleware。该选项类似于COOKIES_DEBUG 内置的scrapy cookies cookie中间件:它为所有请求记录发送和接收的cookie
- SPLASH_LOG_400默认是True - 它指的是Splash记录所有状态码为400的错误。它们很重要,因为它们显示执行Splash脚本时发生的错误。将其设置False为禁用此日志记录
- SPLASH_SLOT_POLICY是scrapy_splash.SlotPolicy.PER_DOMAIN(作为对象,不只是一个字符串)默认情况下。它指定如何为Splash请求维护并发性和礼貌性,并为slot_policy参数 指定默认值SplashRequest,如下所述。
用法
Requests
使用Splash发送请求的最简单方式是使用scrapy_splash.SplashRequest
yield SplashRequest(url, self.parse_result,
# 可选; 传递给Splash HTTP API的参数
args={
'wait': 0.5,
# 'url' 是由请求url预填的
# 对于POST请求,'http_method'设置为'POST'
#'body'被设置为请求POST请求的主体
},
endpoint='render.json', # 可选; 默认render.html
splash_url='<url>', # 可选; 重写SPLASH_URL
slot_policy=scrapy_splash.SlotPolicy.PER_DOMAIN, # 可选
)
或者,您可以使用常规的scrapy. Request 和 ‘splash’ Request meta key
yield scrapy.Request(url, self.parse_result, meta={
'splash': {
# 这里设置渲染参数
'args': {
'html': 1,
'png': 1,
# 'url' 是由请求url预填的
# 对于POST请求, 'http_method'设置为'POST'
# 'body'被设置为请求POST请求的主体
},
# 可选参数
'endpoint': 'render.json', # 可选; 默认为 render.json
'splash_url': '<url>', # 可选; 重写 SPLASH_URL
'slot_policy': scrapy_splash.SlotPolicy.PER_DOMAIN,
'splash_headers': {}, # 可选; 发送给Splash的一个dict类型的headers
'dont_process_response': True, # 可选, 默认为 False
'dont_send_headers': True, # 可选, 默认为 False
'magic_response': False, # 可选, 默认为 True
}
})
request.meta[‘splash’]在middlewares或使用scrapy.Request子类时使用API(SplashFormRequest下面还介绍了这些子类)。例如,meta[‘splash’]允许创建一个中间件,默认情况下,该中间件可以为所有传出的请求启用Splash
博主一句话理解:
使用scrapy.Request去发送一个请求, 加上一个meta[‘Splash’]的键, 就会启用Splash
………..
………..
原理:
加上meta[‘Splash’]键, 系统会在请求的时候自动创建一个中间件,这个中间件就是Splash
SplashRequest是一个方便的工具来填充request.meta[‘splash’], 在大多数情况下应该更容易使用. 对于每个requests.meta[‘splash’] 键 都有一个对应的SplashRequest 关键字参数: 举个例子, 要设置meta[‘splash’][‘args’]使用SplashRequest(…, args=myargs)
博主一句话理解:
SplashRequest就是值, 用来填充meta[‘Splash’]的这个键, 每一个请求的meta[‘Splash’]都有一个SplashRequests关键字
………
………
原理:
meta是元类, 它的格式由dict格式组成, 有key和value,也就是键值对, 有键就会有值
meta[‘splash’][‘args’]包含发送给Splash的参数。scrapy-splash会将一些默认键/值添加到args
- ’url’被设置为request.url;
- 对于POST请求,’http_method’设置为’POST’;
- ‘body’被设置为request.body用于POST请求。
您可以重写来改变默认值
请注意,默认情况下,Scrapy使用AJAX转义方案转义URL片段。如果您想要传递一个带有片段的URL,那么您可以手动设置args参数中的URL。如果您使用SplashRequest,这是自动处理的,但是如果您使用原生的meta[‘splash’] API,您需要记住这一点。
博主个人理解:
你如果使用了SplashRequest会在请求的时候把中文和符号自动转义成url编码的格式, 但如果用的是scrapy.Request + meta[‘Splash’]的时候需要手动的设置args命令中的URL
需要Splash 1.8+来处理POST请求; 在早期版本的’http_method’和’body’参数被忽略。如果您使用/execute 端点并希望支持POST请求,则必须手动处理 Lua脚本中的参数http_method和body参数。
meta[‘splash’][‘cache_args’]是在Splash端缓存的参数名称列表。这些参数仅发送到Splash一次,然后使用缓存值; 它允许节省网络流量并减少请求队列磁盘的内存使用量。使用cache_args仅适用于大型的参数不随每个请求改变; lua_source是一个很好的候选人(如果你不使用字符串格式来构建它)。需要Splash 2.1+才能使用此功能。
博主一句话:
某些lua脚本执行一次通常是费时费力的, 每次请求都执行会耗费很长时间,但是如果开始的时候就执行一遍, 之后直接返回结果,会不会省事很多?
- meta[‘splash’][‘endpoint’]是要使用的Splash端点。如果 使用SplashRequest,则默认使用render.html。如果您使用原始scrapy.Request,则 render.json 是默认值(由于历史原因)。最好是显式地指明端点
博主一句话:
就是执行的api,这个很重要,指明你想要通过哪种api去执行Splash脚本
关于可用的端点和参数的完整列表,请参阅Splash HTTP API文档。
- meta[‘splash’][‘splash_url’]: 覆盖在settings.py中设置的启动URL
- meta[‘splash’][‘splash_headers’]: 允许添加或更改Splash服务器的headers, 注意,此选项不用于设置发送到远程网站的headers。
meta[‘splash’][‘slot_policy’]: 自定义如何保持Splash请求的并发性和礼貌
目前有3个可用的政策:
- scrapy_splash.SlotPolicy.PER_DOMAIN(default) - 根据呈现的URL向下载器 slots发送Splash请求, 如果您希望维护每个域的礼貌和并发设置,那么它是非常有用的。
scrapy_splash.SlotPolicy.SINGLE_SLOT - 将所有Splash请求发送到单个downloader slot, 如果您想限制Splash请求,这很有用。
scrapy_splash.SlotPolicy.SCRAPY_DEFAULT -
不要对slots做任何事情, 它与SINGLE_SLOT类似, 但如果您访问与Splash相同地址的其他服务,则可能会有所不同
meta[‘splash’][‘dont_process_response’] - 设置为True时, SplashMiddleware不会更改对自定义scrapy.Response子类的响应, 默认情况下,SplashResponse、SplashTextResponse或SplashJsonResponse都被传递给回调函数
meta[‘splash’][‘dont_send_headers’]: 默认情况下scrapy-splash通过请求头传递到Splash里“header” JSON POST字段里, 对于所有render.xxx 端点来说, 这意味着默认情况下会遵守Scrapy header选项(参考地址). 在Lua脚本中,可以使用splash的headers 参数splash:go来应用传递的headers: splash:go{url, headers=splash.args.headers}
如果您不想传递headers 给Splash,请将’dont_send_headers’设置为True
meta[‘splash’][‘http_status_from_error_code’] - assert(splash:go(..))失败时将response.status设置为HTTP错误代码; 它需要 meta[‘splash’][‘magic_response’]=True。http_status_from_error_code如果您使用原生meta API,则默认选项为False; SplashRequest默认将其设置为True
meta[‘splash’][‘magic_response’] - 当设置为True时,将从Splash收到一个JSON response, 响应的几个属性(header、body、url、status_code)都是使用JSON返回的数据填充的
- response.headers从’headers’键填充
- response.url 被设置为’url’值
- response.body 被设置为’html’键的值, 或被设置为’body’的base64解码值
response.status 设置为’http_status’ 键的值. 当meta[‘splash’][‘http_status_from_error_code’]状态为True 并且assert(splash:go(..))发生HTTP错误, response.status 也设置为HTTP错误代码
原始的URL、status和headers作为response.real_url, response.splash_response_status和response.splash_response_headers可用.
如果您使用SplashRequest,此选项默认设置为True。 render.json和execute端点可能没有响应中所有必需的键/值。对于非JSON端点,不管magic_response设置如何,只填充url 。
使用scrapy_splash.SplashFormRequest,如果你想通过splash制作一个FormRequest 。它接受与SplashRequest相同的参数,也接受formdata,比如来自scrapy的FormRequest
>>> SplashFormRequest('http://example.com', formdata={'foo': 'bar'})
<POST http://example.com>
SplashFormRequest.from_response也得到了支持,它的工作方式与scrapy文档中描述的一样。
Responses
scrapy-splash可以通过Splash requests返回三大Response子类
- 当结果是图片时, SplashResponse返回二进制Splash响应 - 例如/render.png响应
- 当结果是文本时返回SplashTextResponse - 例如/render.html响应;
- 当结果是一个JSON对象时,返回SplashJsonResponse - 例如,当脚本返回一个Lua表时, 端点为/render.json 或 /execute。
使用标准的Response类设置meta[‘splash’][‘dont_process_response’]=True 或者通过dont_process_response=True 参数交给SplashRequest
所有这些响应都设置了response.url为原始请求的URL(即您想要呈现的网站的url), 而不是请求的Splash端点的URL. “True” URL仍然作为response.real_url可用。
SplashJsonResponse提供额外的特性:
- resposne.data 属性包含从JSON解码的响应数据; 你可以访问它response.data[‘html’]
- 如果配置了Splash会话处理,则可以访问当前Cookie response.cookiejar; 它是一个CookieJar实例
如果在request(默认)中启用了“Scrapy-Splash” 响应魔术, 则会从原始响应主体自动设置几个响应属性(header、body、url、status code)
- response.headers 由“headers” 键填充;
- response.url 被设置为“url” 的值;
- response.body 设置为“html”key的值, 或被设置为’body’的base64解码值;
- response.status 是从’http_status’键的值设置的。
当response.body在SplashJsonResponse(不管是“html”还是“body”键)中更新,response.css和response.xpath 方法都是可用的
关闭对JSON result keys的特殊处理, 请设置meta[‘splash’][‘magic_response’]=False 或者传递magic_response=False参数给SplashRequest
Session Handling(会话处理)
Splash本身是无状态的——每个请求都是一个干净的. 为了支持会话,需要以下内容
- 客户端(Scrapy)必须向Splash发送当前Cookie
- Splash脚本应该使用这些cookie发出请求并从HTTP响应头或JavaScript代码更新它们
- 更新后的cookies应发回给客户
- 客户端应该合并当前的cookie与更新的cookie
对于(2)和(3)Splash提供的splash:get_cookies()和 splash:init_cookies()可用于Splash Lua脚本的方法。
scrapy-splash为(1)和(4)提供帮助: 在“cookie”字段中发送当前cookie, 并从“cookie”响应字段中合并cookie, 设置request.meta[‘splash’][‘session_id’]到会话标识符。如果您只需要一个会话,那么所有请求都使用相同的session_id。任何像“1”或“foo”这样的值都可以。
要使scrapy-splash会话处理起作用,您必须使用/execute端点和一个接受“cookie”参数并在结果中返回“cookie”字段的Lua脚本:
function main(splash)
splash:init_cookies(splash.args.cookies)
-- ... 您的脚本
return {
cookies = splash:get_cookies(),
-- ... 其他的结果,例如html
}
end
SplashRequest会自动为/execute端点设置session_id,例如,如果您使用SplashRequest, /execute端点和兼容的Lua渲染脚本,那么cookie处理是默认启用的。
如果您想要从同一组cookie开始,但是“fork”会话设置除了session_id之外,还会出现request.meta[‘splash’][‘new_session_id’]。 请求cookie将从cookiejar session_id获取, 但是响应cookie将被合并回new_session_id cookiejar。
标准的Scrapy cookies参数可以使用SplashRequest添加cookies到当前的Splash cookiejar
例子
获取HTML内容
import scrapy
from scrapy_splash import SplashRequest
class MySpider(scrapy.Spider):
start_urls = ["http://example.com", "http://example.com/foo"]
def start_requests(self):
for url in self.start_urls:
yield SplashRequest(url, self.parse, args={'wait': 0.5})
def parse(self, response):
# response.body是render.html调用的结果; 它
# 包含由浏览器处理的HTML.
# ...
获取HTML内容和一张屏幕截图
import json
import base64
import scrapy
from scrapy_splash import SplashRequest
class MySpider(scrapy.Spider):
# ...
splash_args = {
'html': 1,
'png': 1,
'width': 600,
'render_all': 1,
}
yield SplashRequest(url, self.parse_result, endpoint='render.json',
args=splash_args)
# ...
def parse_result(self, response):
# 在默认情况下,magic response是开启的,
# 因此在“html” key下的结果可用作response.body
html = response.body
# 您也可以像往常一样查询html结果
title = response.css('title').extract_first()
# 完全解码的JSON数据作为response.data可用
png_bytes = base64.b64decode(response.data['png'])
# ...
运行一个简单的Splash Lua 脚本
import json
import base64
from scrapy_splash import SplashRequest
class MySpider(scrapy.Spider):
# ...
script = """
function main(splash)
assert(splash:go(splash.args.url))
return splash:evaljs("document.title")
end
"""
yield SplashRequest(url, self.parse_result, endpoint='execute',
args={'lua_source': script})
# ...
def parse_result(self, response):
doc_title = response.body_as_unicode()
# ...
更复杂的Splash Lua脚本示例——通过它的CSS选择器获得一个HTML元素的屏幕截图(需要版本2.1+)。注意如何将参数传递给脚本:
import json
import base64
from scrapy_splash import SplashRequest
script = """
-- 参数:
-- * url - 用来呈现的URl;
-- * css - css选择器来呈现;
-- * pad - 截图充填大小.
-- 该函数在内边距周围添加区域
function pad(r, pad)
return {r[1]-pad, r[2]-pad, r[3]+pad, r[4]+pad}
end
-- 主脚本
function main(splash)
-- 该方法返回元素边界盒子
local get_bbox = splash:jsfunc([[
function(css) {
var el = document.querySelector(css);
var r = el.getBoundingClientRect();
return [r.left, r.top, r.right, r.bottom];
}
]])
assert(splash:go(splash.args.url))
assert(splash:wait(0.5))
-- 不要用viewport来裁剪图像
splash:set_viewport_full()
local region = pad(get_bbox(splash.args.css), splash.args.pad)
return splash:png{region=region}
end
"""
class MySpider(scrapy.Spider):
# ...
yield SplashRequest(url, self.parse_element_screenshot,
endpoint='execute',
args={
'lua_source': script,
'pad': 32,
'css': 'a.title'
}
)
# ...
def parse_element_screenshot(self, response):
image_data = response.body # 二进制图像数据的PNG格式
# ...
使用一个Lua脚本获得一个HTML resposnse,其中包含cookie、header、body和方法集,以改变当前的值, lua_source参数值被缓存到Splash服务器上,并不是每一个请求都发送(它需要Splash 2.1+)
import scrapy
from scrapy_splash import SplashRequest
script = """
function main(splash)
splash:init_cookies(splash.args.cookies)
assert(splash:go{
splash.args.url,
headers=splash.args.headers,
http_method=splash.args.http_method,
body=splash.args.body,
})
assert(splash:wait(0.5))
local entries = splash:history()
local last_response = entries[#entries].response
return {
url = splash:url(),
headers = last_response.headers,
http_status = last_response.status,
cookies = splash:get_cookies(),
html = splash:html(),
}
end
"""
class MySpider(scrapy.Spider):
# ...
yield SplashRequest(url, self.parse_result,
endpoint='execute',
cache_args=['lua_source'],
args={'lua_source': script},
headers={'X-My-Header': 'value'},
)
def parse_result(self, response):
# 这里的response.body包含了HTML的结果;
# response.headers从头到尾没有变过
# web page加载交给Splash
# 收集来自所有response和来自JavaScript的cookie
# 然后放入response header里的Set-Cookie,所以Scrapy
# 能够记住他们
HTTP Basic Auth(HTTP基本认证)
如果您需要HTTP Basic Authentication来访问Splash, 请使用Scrapy’s HttpAuthMiddleware
为什么不直接使用Splash HTTP API呢?
明显的替代方法是scrapy-splash将requests直接发送到Splash HTTP API.请看下面的例子,并确保阅读之后的结果
import json
import scrapy
from scrapy.http.headers import Headers
RENDER_HTML_URL = "http://127.0.0.1:8050/render.html"
class MySpider(scrapy.Spider):
start_urls = ["http://example.com", "http://example.com/foo"]
def start_requests(self):
for url in self.start_urls:
body = json.dumps({"url": url, "wait": 0.5}, sort_keys=True)
headers = Headers({'Content-Type': 'application/json'})
yield scrapy.Request(RENDER_HTML_URL, self.parse, method="POST",
body=body, headers=headers)
def parse(self, response):
# 这里的response.body结果是render.html调用的; 它
# 包含由浏览器处理的HTML.
# ...
这很有效,也很简单,但是有些问题你需要注意:
- 有一些样板文件
- 正如我们所看到的Scrapy,我们将requests发送给RENDER_HTML_URL而不是目标url. 它影响并发性和礼貌设置: CONCURRENT_REQUESTS_PER_DOMAIN, DOWNLOAD_DELAY, 由于延迟和并发性设置不再是每个域,所以可以以意想不到的方式运行
- 正如我们所看到的Scrapy,response.url是Splash服务器的url. scrapy-splash将其修复为requests页面的URL. ‘Real’ URL仍然作为response.real_url可用. scrapy-splash也允许透明的处理response.status和response.headers在scrapy上面.
- 一些选项相互依赖 - 例如, 如果您使用了timeout Splash选项,那么您可能需要设置download_timeout scrapy.Request meta key也是同样的
- 很容易把它弄得很微妙, 如果您在准备JSON body时不使用sort_keys=True参数,那么二进制POST体内容可能会发生变化,即使所有的键和值都是相同的,这意味着dupefilter和cache将会不正确地工作。
- 默认的Scrapy duplication filter不会考虑Splash细节. 举个例子, 如果一个URL是在JSON POST request body Scrapy中发送的,那么将会计算request fingerprint,而不需要规范化这个URL。
- Splash 错误请求(HTTP 400)错误是很难调试的,因为默认的response content不显示在Scrapy上面. SplashMiddleware在默认情况下记录HTTP 400的Splash responses(可以通过设置SPLASH_LOG_400 = False选项关闭它)。
- Cookie处理是一项很繁琐的工作,而且在处理Cookie的时候,你也不能使用“Scrapy”内置的Cookie middleware来处理Cookie。
- 对于每个请求(例如lua_source)都不会更改的大的Splash参数, 这样会导致可能会占用大量的空间,从而节省到Scrapy磁盘request队列. scrapy-splash提供了一种只存储一次静态参数的方法。
- Splash 2.1+提供了一种通过在服务器上缓存大型静态参数来节省网络流量的方法,但它需要client支持:client应该发送适当的save_args和load_args值,并处理HTTP 498 responses。
scrapy-splash 功能允许处理这些边界情况和减少引用
获取帮助
- 对于呈现页面的问题,请阅读“Splash FAQ”页面。
对于与Scrapy相关的错误,请查看“报告Scrapy错误”页面。
获得任何其他帮助的最佳方法是在Stack Overflow上提出一个问题。
获取帮助
源代码和bug跟踪器在github上:https://github.com/scrapy-plugins/scrapy-splash
要运行测试,请安装“tox”Python包,然后从源签出运行tox命令。
要运行集成测试,启动溅射并设置SPLASH_URL env变量,在运行tox命令之前将其设置为Splash地址:
docker run -d --rm -p8050:8050 scrapinghub/splash:3.0
SPLASH_URL=http://127.0.0.1:8050 tox -e py36
-- splash主入口
function main(splash, args)
-- 是否执行JavaScript代码, 默认为true
splash.js_enabled = true
-- 如果设置为0或nil(Python中的None),代表不检测超时
splash.resource_timeout = 0.1
-- 图片是否加载, 默认加载
-- 禁用图片加载可能会影响JavaScript渲染
-- 因为禁用图片之后,它的外层DOM节点的高度会受影响,进而影响DOM节点的位置
-- 如果JavaScript对图片节点有操作的话,其执行就会受到影响
-- Splash使用了缓存, 之前加载好的图片可能还会显示出来,这时直接重启Splash即可
splash.images_enabled = true
-- 浏览器插件是否开启, 默认是false
splash.plugins_enabled = false
-- 控制页面上下或左右滚动
-- 控制页面向下滚动400像素值
-- 如果要让页面左右滚动,可以传入x参数
splash.scroll_position = {x=100, y=200}
end
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 变量中包含了错误的原因,否则证明页面加载成功
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, 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()
此方法可以直接调用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
--运行结果
There are 21 DIVs
evaljs()
可以执行JavaScript代码并返回最后一条JavaScript语句的返回结果
local title = splash:evaljs("document.title")
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
autoload()
此方法可以设置每个页面访问时自动加载的对象
ok, reason = splash:autoload{source_or_url, source=nil, url=nil}
参数 | 说明 |
---|---|
source_or_url | JavaScript代码或者JavaScript库链接 |
source | JavaScript代码 |
url | JavaScript库链接 |
但是此方法只负责加载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() 方法加载某些方法库
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
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
-- 运行结果
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": "60.207.237.85",
"url": "http://httpbin.org/get"
}
status: 200
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 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: 200
url: "http://httpbin.org/post"
set_content()
此方法用来设置页面的内容
function main(splash)
assert(splash:set_content("<html><body><h1>hello</h1></body></html>"))
return splash:png()
end
--运行结果
空白页加一个hello字符串
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": "60.207.237.85",
"url": "https://httpbin.org/get"
}
</pre></body></html>
png()
此方法用来获取PNG格式的网页截图
function main(splash, args)
splash:go("https://www.taobao.com")
return splash:png()
end
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
-- 运行结果
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"
add_cookie()
为当前页面添加Cookie
cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}
示例
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
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()
设置当前浏览器页面的大小,即宽高
splash:set_viewport_size(width, height)
示例
function main(splash)
splash:set_viewport_size(400, 700)
assert(splash:go("http://cuiqingcai.com"))
return splash:png()
end
-- 运行结果
返回一个400*700的页面图片
set_viewport_full()
设置浏览器全屏显示
function main(splash)
splash:set_viewport_full()
assert(splash:go("http://cuiqingcai.com"))
return splash:png()
end
set_user_agent()
此方法可以设置浏览器的 User-Agent
-- 将浏览器的 User-Agent 设置为 Splash
function main(splash)
splash:set_user_agent('Splash')
splash:go("http://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": "Splash"
},
"origin": "60.207.237.85",
"url": "http://httpbin.org/get"
}
</pre></body></html>
set_custom_headers()
设置请求头
-- 设置了请求头中的 User-Agent 和 Site 属性
function main(splash)
splash:set_custom_headers({
["User-Agent"] = "Splash",
["Site"] = "Splash",
})
splash:go("http://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",
"Site": "Splash",
"User-Agent": "Splash"
},
"origin": "60.207.237.85",
"url": "http://httpbin.org/get"
}
</pre></body></html>
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
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
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
Splash API调用
Splash给我们提供了一些HTTP API接口,我们只需要请求这些接口并传递相应的参数即可
render.html
此接口用于获取JavaScript渲染的页面的HTML代码
-- render.html接口
http://localhost:8050/render.html?url=网页
-- 等待5秒
url = 'http://localhost:8050/render.html?url=https://www.taobao.com&wait=5'
参数 | 说明 |
---|---|
url | 指定渲染的URL,返回结果即页面渲染后的源代码 |
wait | 指定等待秒数 |
html | 返回结果即会增加源代码数据 |
png | 返回结果即会增加页面PNG截图数据 |
har | 获得页面HAR数据 |
倒数三个配合render.json使用
execute
很多Splash Lua脚本的操作,用此接口便可实现与Lua脚本的对接
render.html接口对于一般的JavaScript渲染页面是足够了, 实现一些交互操作的话,就需要使用execute接口
将此脚本转化为URL编码后的字符串,拼接到execute接口后面
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)