国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---38、動(dòng)態(tài)渲染頁(yè)面抓取:Splash的使用

姘擱『 / 3583人閱讀

摘要:上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)動(dòng)態(tài)渲染頁(yè)面抓取下一篇文章是一個(gè)渲染服務(wù),是一個(gè)帶有的輕量級(jí)瀏覽器,同時(shí)它對(duì)接了中的和庫(kù),利用它我們同樣可以實(shí)現(xiàn)動(dòng)態(tài)渲染頁(yè)面的抓取。

上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---37、動(dòng)態(tài)渲染頁(yè)面抓取:Selenium
下一篇文章:

Splash 是一個(gè) JavaScript 渲染服務(wù),是一個(gè)帶有 HTTP API 的輕量級(jí)瀏覽器,同時(shí)它對(duì)接了 Python 中的 Twisted和 QT 庫(kù),利用它我們同樣可以實(shí)現(xiàn)動(dòng)態(tài)渲染頁(yè)面的抓取。

1. 功能介紹

利用 Splash 我們可以實(shí)現(xiàn)如下功能:

異步方式處理多個(gè)網(wǎng)頁(yè)渲染過(guò)程

獲取渲染后的頁(yè)面的源代碼或截圖

通過(guò)關(guān)閉圖片渲染或者使用 Adblock 規(guī)則來(lái)加快頁(yè)面渲染速度

可執(zhí)行特定的 JavaScript 腳本

可通過(guò) Lua 腳本來(lái)控制頁(yè)面渲染過(guò)程獲取渲染的詳細(xì)過(guò)程并通過(guò) HAR(HTTP Archive)格式呈現(xiàn)

接下來(lái)我們來(lái)了解一下它的具體用法。

2. 準(zhǔn)備工作

在本節(jié)開始之前請(qǐng)確保已經(jīng)正確安裝好了 Splash 并可以正常運(yùn)行服務(wù),如沒(méi)有安裝可以參考第一章的安裝說(shuō)明。

3. 實(shí)例引入

首先我們可以通過(guò) Splash 提供的 Web 頁(yè)面來(lái)測(cè)試 Splash的渲染過(guò)程,例如我們?cè)诒緳C(jī) 8050 端口運(yùn)行了 Splash 服務(wù),打開:http://localhost:8050/ 即可看到其 Web 頁(yè)面,如圖 7-6 所示:

圖 7-6 Web 頁(yè)面
在右側(cè)呈現(xiàn)的是一個(gè)渲染示例,我們可以看到在上方有一個(gè)輸入框,默認(rèn)是:http://google.com,我們?cè)谶@里換成百度測(cè)試一下,將內(nèi)容更改為:https://www.baidu.com,然后點(diǎn)擊按鈕,開始渲染,結(jié)果如圖 7-7 所示:

圖 7-7 運(yùn)行結(jié)果
可以看到網(wǎng)頁(yè)的返回結(jié)果呈現(xiàn)了渲染截圖、HAR 加載統(tǒng)計(jì)數(shù)據(jù)、網(wǎng)頁(yè)的源代碼。
通過(guò) HAR 的結(jié)果我們可以看到 Splash 執(zhí)行了整個(gè)網(wǎng)頁(yè)的渲染過(guò)程,包括 CSS、JavaScript 的加載等過(guò)程,呈現(xiàn)的頁(yè)面和我們?cè)跒g覽器得到的結(jié)果完全一致。
那么這個(gè)過(guò)程是由什么來(lái)控制的呢?我們重新返回首頁(yè)可以看到實(shí)際上是有一段腳本,內(nèi)容如下:

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

這個(gè)腳本是實(shí)際是 Lua 語(yǔ)言寫的腳本,Lua也是一門編程語(yǔ)言,簡(jiǎn)潔易用。
即使我們不懂這個(gè)語(yǔ)言的語(yǔ)法,但通過(guò)腳本表面意思我們也可以大致了解到它是首先調(diào)用 go() 方法去加載頁(yè)面,然后調(diào)用 wait() 方法等待了一定的時(shí)間,最后返回了頁(yè)面的源碼、截圖和 HAR 信息。
所以到這里我們可以大體了解到 Splash 是通過(guò) Lua 腳本來(lái)控制了頁(yè)面的加載過(guò)程,加載過(guò)程完全模擬瀏覽器,最后可返回各種格式的結(jié)果,如網(wǎng)頁(yè)源碼、截圖等。
所以接下來(lái)我們要學(xué)會(huì)用 Splash 的話,一是需要了解其中 Lua 腳本的寫法,二是需要了解相關(guān) API 的用法,那么接下來(lái)我們就來(lái)了解一下這兩部分內(nèi)容。

4. Splash Lua腳本

Splash可以通過(guò)Lua腳本執(zhí)行一系列渲染操作,這樣我們就可以用Splash來(lái)模擬類似Chrome、PhantomJS的操作了。
首先我們先對(duì) Splash Lua 腳本的用法有一個(gè)基本的認(rèn)識(shí),先了解一下它的入口和執(zhí)行方式。

入口及返回值

首先我們來(lái)看一個(gè)基本實(shí)例:

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/ 的代碼編輯區(qū)域,然后點(diǎn)擊按鈕來(lái)測(cè)試一下。
這樣我們就會(huì)看到其返回了網(wǎng)頁(yè)的標(biāo)題,這里我們是通過(guò) evaljs() 方法傳入 JavaScript 腳本,而 document.title 的執(zhí)行結(jié)果就是返回網(wǎng)頁(yè)標(biāo)題,執(zhí)行完畢后賦值給一個(gè) title 變量,隨后將其返回,這樣就可以看到其返回結(jié)果就是網(wǎng)頁(yè)標(biāo)題了,如圖 7-8 所示:

圖 7-8 運(yùn)行結(jié)果
注意到我們?cè)谶@里定義的方法名稱叫做 main(),這個(gè)名稱必須是固定的,Splash 會(huì)默認(rèn)調(diào)用這個(gè)方法。
方法的返回值可以是字典形式、也可以是字符串形式,最后都會(huì)轉(zhuǎn)化為一個(gè) Splash HTTP Response,例如:

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

這樣即返回了一個(gè)字典形式的內(nèi)容。

function main(splash)
??? return "hello"
end

這樣即返回了一個(gè)字符串形式的內(nèi)容,同樣是可以的。

異步處理

Splash是支持異步處理的,但是這里我們并沒(méi)有顯式地指明回調(diào)方法,其回調(diào)的跳轉(zhuǎn)是在Splash內(nèi)部完成的,我們先來(lái)看一個(gè)例子:

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

運(yùn)行后的返回結(jié)果是三個(gè)站點(diǎn)的截圖,如圖 7-9 所示:

圖 7-9 運(yùn)行結(jié)果
在腳本內(nèi)調(diào)用了 wait() 方法,這類似于 Python 中的 sleep(),參數(shù)為等待的秒數(shù),當(dāng) Splash 執(zhí)行到此方法時(shí),它會(huì)轉(zhuǎn)而去處理其他的任務(wù),然后在指定的時(shí)間過(guò)后再回來(lái)繼續(xù)處理。
在這里值得注意的是 Lua 腳本中的字符串拼接,和 Python不同,這里的字符串拼接使用的是 .. 操作符,而不是 +,如有必要可以簡(jiǎn)單了解一下Lua腳本的語(yǔ)法,鏈接:http://www.runoob.com/lua/lua...。
另外這里我們做了加載時(shí)的異常檢測(cè),go() 方法會(huì)返回加載頁(yè)面的結(jié)果狀態(tài),如果頁(yè)面出現(xiàn) 4XX 或 5XX 狀態(tài)碼,ok 變量就會(huì)為空,就不會(huì)返回加載后的圖片。

5. Splash對(duì)象屬性

我們注意到在前面的例子中 main() 方法的第一個(gè)參數(shù)是 splash,這個(gè)對(duì)象非常重要,類似于在 Selenium 中的WebDriver 對(duì)象:

from selenium import webdriver
browser = webdriver.Chrome()

如上所示,現(xiàn)在的 splash 對(duì)象就如同此處 Selenium 中的 browser 對(duì)象,我們可以調(diào)用它的一些屬性和方法來(lái)控制加載過(guò)程,接下來(lái)我們首先看下它的屬性。

args

splash 對(duì)象的 args 屬性可以獲取加載時(shí)配置的參數(shù),它可以獲取加載的 URL,如果為 GET 請(qǐng)求它還可以獲取 GET 請(qǐng)求參數(shù),如果為 POST 請(qǐng)求它可以獲取表單提交的數(shù)據(jù)。Splash 支持第二個(gè)參數(shù)直接作為 args,例如:

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

在這里第二個(gè)參數(shù) args 就相當(dāng)于 splash.args 屬性,以上代碼等價(jià)于:

function main(splash)
??? local url = splash.args.url
end
js_enabled

這個(gè)屬性是 Splash 的 JavaScript 執(zhí)行開關(guān),我們可以將其配置為 True 或 False 來(lái)控制是否可以執(zhí)行 JavaScript 代碼,默認(rèn)為 True,例如我們?cè)谶@里禁用一下 JavaScript 的執(zhí)行:

function main(splash, args)
? splash:go("https://www.baidu.com")
? splash.js_enabled = false
? local title = splash:evaljs("document.title")
? return {title=title}
end

禁用之后,我們重新調(diào)用了 evaljs() 方法執(zhí)行 JavaScript 代碼,那么運(yùn)行結(jié)果就會(huì)拋出異常:

{
??? "error": 400,
??? "type": "ScriptError",
??? "info": {
??????? "type": "JS_ERROR",
??????? "js_error_message": null,
??????? "source": "[string "function main(splash, args)
..."]",
??????? "message": "[string "function main(splash, args)
..."]:4: unknown JS error: None",
??????? "line_number": 4,
??????? "error": "unknown JS error: None",
??????? "splash_method": "evaljs"
??? },
??? "description": "Error happened while executing Lua script"
}

不過(guò)一般來(lái)說(shuō)我們不用設(shè)置此屬性開關(guān),默認(rèn)開啟即可。

resource_timeout

此屬性可以設(shè)置加載的超時(shí)時(shí)間,單位是秒,如果設(shè)置為 0或 nil(類似 Python 中的 None)就代表不檢測(cè)超時(shí),我們用一個(gè)實(shí)例感受一下:

function main(splash)
??? splash.resource_timeout = 0.1
??? assert(splash:go("https://www.taobao.com"))
??? return splash:png()
end

例如這里我們將超時(shí)時(shí)間設(shè)置為 0.1 秒,如果在 0.1 秒之內(nèi)沒(méi)有得到響應(yīng)就會(huì)拋出異常,錯(cuò)誤如下:

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

此屬性適合在網(wǎng)頁(yè)加載速度較慢的情況下設(shè)置,如果超過(guò)了某個(gè)時(shí)間無(wú)響應(yīng)則直接拋出異常并忽略即可。

images_enabled

此屬性可以設(shè)置圖片是否加載,默認(rèn)情況下是加載的,但是禁用之后可以節(jié)省網(wǎng)絡(luò)流量并提高網(wǎng)頁(yè)加載速度,但是值得注意的是禁用圖片加載之后可能會(huì)影響 JavaScript 渲染,因?yàn)榻脠D片之后它的外層 DOM 節(jié)點(diǎn)的高度會(huì)受影響,進(jìn)而影響 DOM 節(jié)點(diǎn)的位置,所以如果 JavaScript 如果使用了相關(guān)變量的話,其執(zhí)行就會(huì)受到影響,不過(guò)一般情況下不會(huì)。
另外值得注意的是 Splash 使用了緩存,所以如果你一開始加載出來(lái)了網(wǎng)頁(yè)圖片,然后禁用了圖片加載,然后再重新加載頁(yè)面,之前加載好的圖片可能還會(huì)顯示出來(lái),這時(shí)可以重啟一下 Splash 即可解決。
禁用圖片加載的示例如下:

function main(splash, args)
? splash.images_enabled = false
? assert(splash:go("https://www.jd.com"))
? return {png=splash:png()}
end

這樣返回的頁(yè)面截圖就不會(huì)帶有任何圖片,加載速度也會(huì)快很多。

plugins_enabled

此屬性可以控制瀏覽器插件是否開啟,如 Flash 插件。默認(rèn)此屬性是 False 不開啟,可以使用如下代碼控制其開啟和關(guān)閉:

splash.plugins_enabled = true/false
scroll_position

此屬性可以控制頁(yè)面的滾動(dòng)偏移,通過(guò)設(shè)置此屬性我們可以控制頁(yè)面上下或左右滾動(dòng),還是比較常用的一個(gè)屬性,我們用一個(gè)實(shí)例感受一下:

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

這樣我們就可以控制頁(yè)面向下滾動(dòng) 400 像素值,結(jié)果如圖 7-10 所示:

圖 7-10 運(yùn)行結(jié)果
如果要控制左右滾動(dòng)可以傳入 x 參數(shù),代碼如下:

splash.scroll_position = {x=100, y=200}
6. Splash對(duì)象方法 go()

go() 方法就是用來(lái)請(qǐng)求某個(gè)鏈接的方法,而且它可以模擬 GET 和 POST 請(qǐng)求,同時(shí)支持傳入 Headers、Form Data 等數(shù)據(jù),用法如下:

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

參數(shù)說(shuō)明如下:

url,即請(qǐng)求的URL。

baseurl,可選參數(shù),默認(rèn)為空,資源加載相對(duì)路徑。

headers,可選參數(shù),默認(rèn)為空,請(qǐng)求的 Headers。

http_method,可選參數(shù),默認(rèn)為 GET,同時(shí)支持 POST。

body,可選參數(shù),默認(rèn)為空,POST 的時(shí)候的表單數(shù)據(jù),使用的 Content-type 為 application/json。

formdata,可選參數(shù),默認(rèn)為空,POST 的時(shí)候表單數(shù)據(jù),使用的 Content-type為application/x-www-form-urlencoded。

返回的結(jié)果是結(jié)果 ok 和原因 reason 的組合,如果 ok 為空,代表網(wǎng)頁(yè)加載出現(xiàn)了錯(cuò)誤,此時(shí) reason 變量中包含了錯(cuò)誤的原因,否則證明頁(yè)面加載成功,示例如下:

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

在這里我們模擬了一個(gè) POST 請(qǐng)求,并傳入了 POST 的表單數(shù)據(jù),如果成功,則返回頁(yè)面源代碼。
運(yùn)行結(jié)果如下:

{
  "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"
}

通過(guò)結(jié)果可以看到我們成功實(shí)現(xiàn)了 POST 請(qǐng)求并發(fā)送了表單數(shù)據(jù)。

wait()

此方法可以控制頁(yè)面等待時(shí)間,使用方法如下:

ok, reason = splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}

參數(shù)說(shuō)明如下:

time,等待的秒數(shù)。

cancel_on_redirect,可選參數(shù),默認(rèn) False,如果發(fā)生了重定向就停止等待,并返回重定向結(jié)果。

cancel_on_error,可選參數(shù),默認(rèn) False,如果發(fā)生了加載錯(cuò)誤就停止等待。

返回結(jié)果同樣是結(jié)果 ok 和原因 reason 的組合。
我們用一個(gè)實(shí)例感受一下:

function main(splash)
??? splash:go("https://www.taobao.com")
??? splash:wait(2)
??? return {html=splash:html()}
end

如上代碼可以實(shí)現(xiàn)訪問(wèn)淘寶并等待 2 秒,隨后返回頁(yè)面源代碼的功能。

jsfunc()

此方法可以直接調(diào)用 JavaScript 定義的方法,需要用雙中括號(hào)包圍,相當(dāng)于實(shí)現(xiàn)了 JavaScript 方法到 Lua 腳本的轉(zhuǎn)換,示例如下:

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

運(yùn)行結(jié)果:

There are 21 DIVs

首選我們聲明了一個(gè)方法,然后在頁(yè)面加載成功后調(diào)用了此方法計(jì)算出了頁(yè)面中的 div 節(jié)點(diǎn)的個(gè)數(shù)。
但這只是 Splash 提供的 Web 頁(yè)面功能,更多的功能我們可以使用它提供的 HTTP API 來(lái)完成 JavaScript 渲染過(guò)程。
關(guān)于更多 JavaScript 到 Lua 腳本的轉(zhuǎn)換細(xì)節(jié)可以參考官方文檔介紹:https://splash.readthedocs.io...。

evaljs()

此方法可以執(zhí)行 JavaScript 代碼并返回最后一條語(yǔ)句的返回結(jié)果,使用方法如下:

result = splash:evaljs(js)

比如我們可以用下面的代碼來(lái)獲取頁(yè)面的標(biāo)題:

local title = splash:evaljs("document.title")
runjs()

此方法可以執(zhí)行 JavaScript 代碼,和 evaljs() 功能類似,但是此方法更偏向于執(zhí)行某些動(dòng)作或聲明某些方法,evaljs() 偏向于獲取某些執(zhí)行結(jié)果,例如:

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() 先聲明了一個(gè) JavaScript 定義的方法,然后通過(guò) evaljs() 來(lái)調(diào)用得到結(jié)果。
運(yùn)行結(jié)果如下:

bar
autoload()

此方法可以設(shè)置在每個(gè)頁(yè)面訪問(wèn)時(shí)自動(dòng)加載的對(duì)象,使用方法如下:

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

參數(shù)說(shuō)明如下:

source_or_url,JavaScript 代碼或者 JavaScript 庫(kù)鏈接。

source,JavaScript 代碼。

url,JavaScript 庫(kù)鏈接

但是此方法只負(fù)責(zé)加載 JavaScript 代碼或庫(kù),不執(zhí)行任何操作,如果要執(zhí)行操作可以調(diào)用 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

在這里我們調(diào)用 autoload() 聲明了一個(gè) JavaScript 方法,然后通過(guò) evaljs() 調(diào)用了此方法執(zhí)行。
運(yùn)行結(jié)果:

百度一下,你就知道

另外我們也可以加載某些方法庫(kù),如 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

運(yùn)行結(jié)果:

JQuery version: 2.1.3
call_later()

此方法可以通過(guò)設(shè)置定時(shí)任務(wù)和延遲時(shí)間實(shí)現(xiàn)任務(wù)延時(shí)執(zhí)行,并且可以在執(zhí)行前通過(guò) cancel() 方法重新執(zhí)行定時(shí)任務(wù),示例如下:

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

在這里我們?cè)O(shè)置了一個(gè)定時(shí)任務(wù),0.2 秒的時(shí)候獲取網(wǎng)頁(yè)截圖,然后等待 1 秒,1.2 秒時(shí)再次獲取網(wǎng)頁(yè)截圖,訪問(wèn)的頁(yè)面是淘寶,最后將截圖結(jié)果返回。
運(yùn)行結(jié)果如圖 7-11 所示:

圖 7-11 運(yùn)行結(jié)果
我們可以發(fā)現(xiàn)第一次截圖網(wǎng)頁(yè)還沒(méi)有加載出來(lái),截圖為空,第二次網(wǎng)頁(yè)便加載成功了。

http_get()

此方法可以模擬發(fā)送 HTTP 的 GET 請(qǐng)求,使用方法如下:

response = splash:http_get{url, headers=nil, follow_redirects=true}

參數(shù)說(shuō)明如下:

url,請(qǐng)求URL。

headers,可選參數(shù),默認(rèn)為空,請(qǐng)求的 Headers。

follow_redirects,可選參數(shù),默認(rèn)為 True,是否啟動(dòng)自動(dòng)重定向。我們用一個(gè)實(shí)例來(lái)感受一下:

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

運(yùn)行結(jié)果:

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() 方法類似,此方法是模擬發(fā)送一個(gè) POST 請(qǐng)求,不過(guò)多了一個(gè)參數(shù) body,使用方法如下:

response = splash:http_post{url, headers=nil, follow_redirects=true, body=nil}

參數(shù)說(shuō)明如下:

url,請(qǐng)求URL。

headers,可選參數(shù),默認(rèn)為空,請(qǐng)求的 Headers。

follow_redirects,可選參數(shù),默認(rèn)為 True,是否啟動(dòng)自動(dòng)重定向。body,可選參數(shù),默認(rèn)為空,即表單數(shù)據(jù)。我們用一個(gè)實(shí)例感受一下:

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

運(yùn)行結(jié)果:

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"

可以看到在這里我們成功模擬提交了 POST 請(qǐng)求并發(fā)送了表單數(shù)據(jù)。

set_content()

此方法可以用來(lái)設(shè)置頁(yè)面的內(nèi)容,示例如下:

function main(splash)
??? assert(splash:set_content("

hello

")) ??? return splash:png() end

運(yùn)行結(jié)果如圖 7-12 所示:

圖 7-12 運(yùn)行結(jié)果

html()

此方法可以用來(lái)獲取網(wǎng)頁(yè)的源代碼,非常簡(jiǎn)單又常用的方法,示例如下:

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

運(yùn)行結(jié)果:

{
  "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"
}
png()

此方法可以用來(lái)獲取 PNG 格式的網(wǎng)頁(yè)截圖,示例如下:

function main(splash, args)
? splash:go("https://www.taobao.com")
? return splash:png()
end
jpeg()

此方法可以用來(lái)獲取 JPEG 格式的網(wǎng)頁(yè)截圖,示例如下:

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

此方法可以用來(lái)獲取頁(yè)面加載過(guò)程描述,示例如下:

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

運(yùn)行結(jié)果如圖 7-13 所示:

圖 7-13 運(yùn)行結(jié)果
在這里顯示了頁(yè)面加載過(guò)程中的每個(gè)請(qǐng)求記錄詳情。

url()

此方法可以獲取當(dāng)前正在訪問(wèn)的 URL,示例如下:

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

運(yùn)行結(jié)果如下:

https://www.baidu.com/
get_cookies()

此方法可以獲取當(dāng)前頁(yè)面的 Cookies,示例如下:

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

運(yùn)行結(jié)果如下:

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()

此方法可以為當(dāng)前頁(yè)面添加 Cookie,用法如下:

cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}

方法的各個(gè)參數(shù)代表了 Cookie 的各個(gè)屬性。
示例如下:

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

在這里我們清除了所有的 Cookies,然后再調(diào)用 get_cookies() 并將結(jié)果返回。
運(yùn)行結(jié)果:

Splash Response: Array[0]

可以看到Cookies被全部清空,沒(méi)有任何結(jié)果。

get_viewport_size()

此方法可以獲取當(dāng)前瀏覽器頁(yè)面的大小,即寬高,示例如下:

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

運(yùn)行結(jié)果:

Splash Response: Array[2]
0: 1024
1: 768
set_viewport_size()

此方法可以設(shè)置當(dāng)前瀏覽器頁(yè)面的大小,即寬高,用法如下:

splash:set_viewport_size(width, height)

例如這里我們?cè)L問(wèn)一個(gè)寬度自適應(yīng)的頁(yè)面,示例如下:

function main(splash)
??? splash:set_viewport_size(400, 700)
??? assert(splash:go("http://cuiqingcai.com"))
??? return splash:png()
end

運(yùn)行結(jié)果如圖 7-14 所示:

圖 7-14 運(yùn)行結(jié)果

set_viewport_full()

此方法可以設(shè)置瀏覽器全屏顯示,示例如下:

function main(splash)
??? splash:set_viewport_full()
??? assert(splash:go("http://cuiqingcai.com"))
??? return splash:png()
end
set_user_agent()

此方法可以設(shè)置瀏覽器的 User-Agent,示例如下:

function main(splash)
? splash:set_user_agent("Splash")
? splash:go("http://httpbin.org/get")
? return splash:html()
end

在這里我們將瀏覽器的 User-Agent 設(shè)置為 Splash,運(yùn)行結(jié)果如下:

{
  "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"
}

可以看到此處 User-Agent 被成功設(shè)置。

set_custom_headers()

此方法可以設(shè)置請(qǐng)求的 Headers,示例如下:

function main(splash)
? splash:set_custom_headers({
???? ["User-Agent"] = "Splash",
???? ["Site"] = "Splash",
? })
? splash:go("http://httpbin.org/get")
? return splash:html()
end

在這里我們?cè)O(shè)置了 Headers 中的 User-Agent 和 Site 屬性,運(yùn)行結(jié)果:

{
  "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"
}

可以看到結(jié)果的 Headers 中兩個(gè)字段被成功設(shè)置。

select()

select() 方法可以選中符合條件的第一個(gè)節(jié)點(diǎn),如果有多個(gè)節(jié)點(diǎn)符合條件,則只會(huì)返回一個(gè),其參數(shù)是 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

在這里我們首先訪問(wèn)了百度,然后選中了搜索框,隨后調(diào)用了 send_text() 方法填寫了文本,然后返回網(wǎng)頁(yè)截圖。
結(jié)果如圖 7-15 所示:

圖 7-15 運(yùn)行結(jié)果
可以看到我們成功填寫了輸入框。

select_all()

此方法可以選中所有的符合條件的節(jié)點(diǎn),其參數(shù)是 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

在這里我們通過(guò) CSS 選擇器選中了節(jié)點(diǎn)的正文內(nèi)容,隨后遍歷了所有節(jié)點(diǎn),然后將其中的文本獲取了下來(lái)。
運(yùn)行結(jié)果:

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.”"

可以發(fā)現(xiàn)我們成功將 10 個(gè)節(jié)點(diǎn)的正文內(nèi)容獲取了下來(lái)。

mouse_click()

此方法可以模擬鼠標(biāo)點(diǎn)擊操作,傳入的參數(shù)為坐標(biāo)值 x、y,也可以直接選中某個(gè)節(jié)點(diǎn)直接調(diào)用此方法,示例如下:

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

在這里我們首先選中了頁(yè)面的輸入框,輸入了文本,然后選中了提交按鈕,調(diào)用了 mouse_click() 方法提交查詢,然后頁(yè)面等待三秒,返回截圖,結(jié)果如圖 7-16 所示:

圖 7-16 運(yùn)行結(jié)果
可以看到在這里我們成功獲取了查詢后的頁(yè)面內(nèi)容,模擬了百度搜索操作。
以上我們介紹了 Splash 的常用 API 操作,還有一些 API 在這不再一一介紹,更加詳細(xì)和權(quán)威的說(shuō)明可以參見(jiàn)官方文檔:https://splash.readthedocs.io...,此頁(yè)面介紹了 splash 對(duì)象的所有 API 操作,另外還有針對(duì)于頁(yè)面元素的 API 操作,鏈接為:https://splash.readthedocs.io...。

7. Splash API調(diào)用

在上文中我們說(shuō)明了 Splash Lua 腳本的用法,但這些腳本是在 Splash 頁(yè)面里面測(cè)試運(yùn)行的,我們?nèi)绾尾拍芾肧plash 來(lái)渲染頁(yè)面呢?怎樣才能和 Python 程序結(jié)合使用并抓取 JavaScript 渲染的頁(yè)面呢?
其實(shí) Splash 給我們提供了一些 HTTP API 接口,我們只需要請(qǐng)求這些接口并傳遞相應(yīng)的參數(shù)即可獲取頁(yè)面渲染后的結(jié)果,下面我們對(duì)這些接口進(jìn)行介紹:

render.html

此接口用于獲取 JavaScript 渲染的頁(yè)面的 HTML 代碼,接口地址就是 Splash 的運(yùn)行地址加此接口名稱,例如:http://localhost:8050/render.html,我們可以用 curl 來(lái)測(cè)試一下:

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

我們給此接口傳遞了一個(gè) url 參數(shù)指定渲染的 URL,返回結(jié)果即頁(yè)面渲染后的源代碼。
如果用 Python 實(shí)現(xiàn)的話,代碼如下:

import requests
url = "http://localhost:8050/render.html?url=https://www.baidu.com"
response = requests.get(url)
print(response.text)

這樣我們就可以成功輸出百度頁(yè)面渲染后的源代碼了。
另外此接口還可以指定其他參數(shù),如 wait 指定等待秒數(shù),如果我們要確保頁(yè)面完全加載出來(lái)可以增加等待時(shí)間,例如:

import requests
url = "http://localhost:8050/render.html?url=https://www.taobao.com&wait=5"
response = requests.get(url)
print(response.text)

如果增加了此等待時(shí)間后,得到響應(yīng)的時(shí)間就會(huì)相應(yīng)變長(zhǎng),如在這里我們會(huì)等待大約 5 秒多鐘即可獲取 JavaScript 渲染后的淘寶頁(yè)面源代碼。
另外此接口還支持代理設(shè)置、圖片加載設(shè)置、Headers設(shè)置、請(qǐng)求方法設(shè)置,具體的用法可以參見(jiàn)官方文檔:https://splash.readthedocs.io...。

render.png

此接口可以獲取網(wǎng)頁(yè)截圖,參數(shù)相比 render.html 又多了幾個(gè),如 width、height 來(lái)控制寬高,返回的是 PNG 格式的圖片二進(jìn)制數(shù)據(jù)。
示例如下:

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

在這里我們還傳入了 width 和 height 來(lái)放縮頁(yè)面大小為 1000x700 像素。
如果用 Python 實(shí)現(xiàn),我們可以將返回的二進(jìn)制數(shù)據(jù)保存為PNG 格式的圖片,實(shí)現(xiàn)如下:

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 運(yùn)行結(jié)果
這樣我們就成功獲取了京東首頁(yè)渲染完成后的頁(yè)面截圖,詳細(xì)的參數(shù)設(shè)置可以參考官網(wǎng)文檔:https://splash.readthedocs.io...。

render.jpeg

此接口和 render.png 類似,不過(guò)它返回的是 JPEG 格式的圖片二進(jìn)制數(shù)據(jù)。
另外此接口相比 render.png 還多了一個(gè)參數(shù) quality,可以用來(lái)設(shè)置圖片質(zhì)量。

render.har

此接口用于獲取頁(yè)面加載的 HAR 數(shù)據(jù),示例如下:

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

返回結(jié)果非常多,是一個(gè) Json 格式的數(shù)據(jù),里面包含了頁(yè)面加載過(guò)程中的 HAR 數(shù)據(jù)。
結(jié)果如圖 7-18 所示:

圖 7-18 運(yùn)行結(jié)果

render.json

此接口包含了前面接口的所有功能,返回結(jié)果是 Json 格式,示例如下:

curl http://localhost:8050/render.json?url=https://httpbin.org

結(jié)果如下:

{"title": "httpbin(1): HTTP Client Testing Service", "url": "https://httpbin.org/", "requestedUrl": "https://httpbin.org/", "geometry": [0, 0, 1024, 768]}

可以看到這里以 Json 形式返回了相應(yīng)的請(qǐng)求數(shù)據(jù)。
我們可以通過(guò)傳入不同的參數(shù)控制其返回的結(jié)果,如傳入html=1,返回結(jié)果即會(huì)增加源代碼數(shù)據(jù),傳入 png=1,返回結(jié)果機(jī)會(huì)增加頁(yè)面 PNG 截圖數(shù)據(jù),傳入har=1則會(huì)獲得頁(yè)面 HAR 數(shù)據(jù),例如:

curl http://localhost:8050/render.json?url=https://httpbin.org&html=1&har=1

這樣返回的 Json 結(jié)果便會(huì)包含網(wǎng)頁(yè)源代碼和 HAR 數(shù)據(jù)。
還有更多參數(shù)設(shè)置可以參考官方文檔:https://splash.readthedocs.io...。

execute

此接口才是最為強(qiáng)大的接口,我們?cè)谇懊嬲f(shuō)了很多 Splash Lua 腳本的操作,用此接口便可以實(shí)現(xiàn)和 Lua 腳本的對(duì)接。
前面的 render.html、render.png 等接口對(duì)于一般的 JavaScript 渲染頁(yè)面是足夠了,但是如果要實(shí)現(xiàn)一些交互操作的話還是無(wú)能為力的,所以這里就需要使用此 execute 接口來(lái)對(duì)接 Lua 腳本和網(wǎng)頁(yè)進(jìn)行交互了。
我們先實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的腳本,直接返回?cái)?shù)據(jù):

function main(splash)
??? return "hello"
end

然后將此腳本轉(zhuǎn)化為 URL 編碼后的字符串,拼接到 execute 接口后面,示例如下:

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

運(yùn)行結(jié)果:

hello

在這里我們通過(guò) lua_source 參數(shù)傳遞了轉(zhuǎn)碼后的 Lua 腳本,通過(guò) execute 接口獲取了最終腳本的執(zhí)行結(jié)果。
那么在這里我們更加關(guān)心的肯定是如何用 Python 來(lái)實(shí)現(xiàn),上例用 Python 實(shí)現(xiàn)如下:

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)

運(yùn)行結(jié)果:

hello

在這里我們用 Python 中的三引號(hào)來(lái)將 Lua 腳本包括起來(lái),然后用 urllib.parse 模塊里的 quote()方法將腳本進(jìn)行 URL 轉(zhuǎn)碼,隨后構(gòu)造了 Splash 請(qǐng)求 URL,將其作為 lua_source 參數(shù)傳遞,這樣運(yùn)行結(jié)果就會(huì)顯示 Lua 腳本執(zhí)行后的結(jié)果。
我們?cè)賮?lái)一個(gè)實(shí)例看一下:

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)

運(yùn)行結(jié)果:

{"url": "http://httpbin.org/get", "status": 200, "html": "{
? "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"
}
"}

返回結(jié)果是 Json 形式,我們成功獲取了請(qǐng)求的 URL、狀態(tài)碼和網(wǎng)頁(yè)源代碼。
如此一來(lái),我們之前所說(shuō)的 Lua 腳本均可以用此方式與 Python 進(jìn)行對(duì)接,這樣的話,所有網(wǎng)頁(yè)的動(dòng)態(tài)渲染、模擬點(diǎn)擊、表單提交、頁(yè)面滑動(dòng)、延時(shí)等待后的一些結(jié)果均可以自由控制,獲取頁(yè)面源碼和截圖都不在話下。

8、Splash負(fù)載均衡配置

如果我們用 Splash 來(lái)做 JavaScript 動(dòng)態(tài)渲染的頁(yè)面的抓取的話,如果爬取的量非常大,任務(wù)非常多,如果我們用一個(gè) Splash 服務(wù)來(lái)處理的話未免壓力太大了,所以我們可以考慮搭建一個(gè)負(fù)載均衡器來(lái)把壓力分散到各個(gè)服務(wù)器上,這樣相當(dāng)于多臺(tái)機(jī)器多個(gè)服務(wù)共同參與任務(wù)的處理,可以減小單個(gè) Splash 服務(wù)的壓力。

1. 配置Splash服務(wù)

要搭建 Splash 負(fù)載均衡首先我們需要有多個(gè) Splash 服務(wù),假如在這里我在四臺(tái)遠(yuǎn)程主機(jī)的 8050 端口上都開啟了 Splash 服務(wù),它們的服務(wù)地址分別為:41.159.27.223:8050、41.159.27.221:8050、41.159.27.9:8050、41.159.117.119:8050,四個(gè)服務(wù)完全一致,都是通過(guò) Docker 的 Splash 鏡像開啟的,訪問(wèn)任何一個(gè)服務(wù)都可以使用 Splash 服務(wù)。

2. 配置負(fù)載均衡

接下來(lái)我們可以選用任意一臺(tái)帶有公網(wǎng) IP 的主機(jī)來(lái)配置負(fù)載均衡,首先需要在這臺(tái)主機(jī)上裝好 Nginx,然后修改 Nginx 的配置文件 nginx.conf,添加如下內(nèi)容:

http {
    upstream splash {
        least_conn;
        server 41.159.27.223:8050;
        server 41.159.27.221:8050;
        server 41.159.27.9:8050;
        server 41.159.117.119:8050;
    }
    server {
        listen 8050;
        location / {
            proxy_pass http://splash;
        }
    }
}

這樣我們通過(guò) upstream 字段定義了一個(gè)名字叫做 splash 的服務(wù)集群配置,least_conn 代表最少鏈接負(fù)載均衡,它適合處理請(qǐng)求處理時(shí)間長(zhǎng)短不一造成服務(wù)器過(guò)載的情況。

或者我們也可以不指定配置,配置如下:

upstream splash {
    server 41.159.27.223:8050;
    server 41.159.27.221:8050;
    server 41.159.27.9:8050;
    server 41.159.117.119:8050;
}

這樣默認(rèn)以輪詢策略實(shí)現(xiàn)負(fù)載均衡,每個(gè)服務(wù)器的壓力相同,此策略適合服務(wù)器配置相當(dāng),無(wú)狀態(tài)且短平快的服務(wù)使用。

另外我們還可以指定權(quán)重,配置如下:

upstream splash {
    server 41.159.27.223:8050 weight=4;
    server 41.159.27.221:8050 weight=2;
    server 41.159.27.9:8050 weight=2;
    server 41.159.117.119:8050 weight=1;
}

我們通過(guò) weight 指定了各個(gè)服務(wù)的權(quán)重,權(quán)重越高分配到處理的請(qǐng)求越多,假如不同的服務(wù)器配置差別比較大的話,就可以使用此種配置。

最后還有一種 IP 哈希負(fù)載均衡,配置如下:

upstream splash {
    ip_hash;
    server 41.159.27.223:8050;
    server 41.159.27.221:8050;
    server 41.159.27.9:8050;
    server 41.159.117.119:8050;
}

服務(wù)器根據(jù)請(qǐng)求客戶端的 IP 地址進(jìn)行哈希計(jì)算,確保使用同一個(gè)服務(wù)器響應(yīng)請(qǐng)求,這種策略適合有狀態(tài)的服務(wù),如用戶登錄后訪問(wèn)某個(gè)頁(yè)面的情形。不過(guò)對(duì)于 Splash 來(lái)說(shuō)不需要。

我們可以根據(jù)不同的情形選用不同的配置,配置完成后重啟一下 Nginx 服務(wù):

sudo nginx -s reload

這樣直接訪問(wèn) Nginx 所在服務(wù)器的 8050 端口即可實(shí)現(xiàn)負(fù)載均衡了。

3. 配置認(rèn)證

現(xiàn)在 Splash 是公開訪問(wèn)的,如果我們不想讓其被公開訪問(wèn)還可以配置認(rèn)證,仍然借助于 Nginx 即可,可以在 server 的 location 字段中添加一個(gè) auth_basic 和 auth_basic_user_file 字段,配置如下:

http {
    upstream splash {
        least_conn;
        server 41.159.27.223:8050;
        server 41.159.27.221:8050;
        server 41.159.27.9:8050;
        server 41.159.117.119:8050;
    }
    server {
        listen 8050;
        location / {
            proxy_pass http://splash;
            auth_basic "Restricted";
            auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
        }
    }
}

在這里使用的用戶名密碼配置放置在 /etc/nginx/conf.d 目錄,我們需要使用 htpasswd 命令創(chuàng)建,例如創(chuàng)建一個(gè)用戶名為 admin 的文件,命令如下:

htpasswd -c .htpasswd admin

接下就會(huì)提示我們輸入密碼,輸入兩次之后,就會(huì)生成密碼文件,查看一下內(nèi)容:

cat .htpasswd 
admin:5ZBxQr0rCqwbc

配置完成之后我們重啟一下 Nginx 服務(wù),運(yùn)行如下命令:

sudo nginx -s reload

這樣訪問(wèn)認(rèn)證就成功配置好了。

4. 測(cè)試

最后我們可以用代碼來(lái)測(cè)試一下負(fù)載均衡的配置,看看到底是不是每次請(qǐng)求會(huì)切換IP,利用 http://httpbin.org/get 測(cè)試即可,代碼實(shí)現(xiàn)如下:

import requests
from urllib.parse import quote
import re

lua = """
function main(splash, args)
  local treat = require("treat")
  local response = splash:http_get("http://httpbin.org/get")
  return treat.as_string(response.body)
end
"""

url = "http://splash:8050/execute?lua_source=" + quote(lua)
response = requests.get(url, auth=("admin", "admin"))
ip = re.search("(d+.d+.d+.d+)", response.text).group(1)
print(ip)

這里的 URL 中的 splash 請(qǐng)自行替換成自己的 Nginx 服務(wù)器 IP,在這里我修改了 Hosts 添加了 splash 別名。

多次運(yùn)行代碼之后可以發(fā)現(xiàn)每次請(qǐng)求的 IP 都會(huì)變化:

如第一次的結(jié)果:

41.159.27.223

第二次的結(jié)果:

41.159.27.9

這就說(shuō)明負(fù)載均衡已經(jīng)成功實(shí)現(xiàn)了。

9. 結(jié)語(yǔ)

到現(xiàn)在為止,我們就可以用 Python 和 Splash 實(shí)現(xiàn)JavaScript 渲染的頁(yè)面的抓取了,除了 Selenium,本節(jié)所說(shuō)的 Splash 同樣可以做到非常強(qiáng)大的渲染功能,同時(shí)它也不需要瀏覽器即可渲染,使用非常方便。

本節(jié)我們還成功實(shí)現(xiàn)了負(fù)載均衡的配置,配置了負(fù)載均衡之后可以多個(gè) Splash 服務(wù)共同合作,減輕單個(gè)服務(wù)的負(fù)載,還是比較有用的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/44107.html

相關(guān)文章

  • Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---37、動(dòng)態(tài)渲染頁(yè)面抓取:Selenium

    摘要:不過(guò)動(dòng)態(tài)渲染的頁(yè)面不止這一種。再有淘寶這種頁(yè)面,它即使是獲取的數(shù)據(jù),但是其接口含有很多加密參數(shù),我們難以直接找出其規(guī)律,也很難直接分析來(lái)抓取。我們用一個(gè)實(shí)例來(lái)感受一下在這里們依然是先打開知乎頁(yè)面,然后獲取提問(wèn)按鈕這個(gè)節(jié)點(diǎn),再將其 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---36、分析Ajax爬取今日頭條街拍美圖下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---38、動(dòng)態(tài)渲染頁(yè)面抓取:Spla...

    zhjx922 評(píng)論0 收藏0
  • Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---17、爬蟲基本原理

    摘要:在前面我們講到了和的概念,我們向網(wǎng)站的服務(wù)器發(fā)送一個(gè),返回的的便是網(wǎng)頁(yè)源代碼。渲染頁(yè)面有時(shí)候我們?cè)谟没蜃ト【W(wǎng)頁(yè)時(shí),得到的源代碼實(shí)際和瀏覽器中看到的是不一樣的。所以使用基本請(qǐng)求庫(kù)得到的結(jié)果源代碼可能跟瀏覽器中的頁(yè)面源代碼不太一樣。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---16、Web網(wǎng)頁(yè)基礎(chǔ)下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---18、Session和Cookies 爬蟲,即網(wǎng)...

    hellowoody 評(píng)論0 收藏0
  • Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---11、爬蟲框架安裝:ScrapySplash、ScrapyRedi

    摘要:上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)爬蟲框架的安裝下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)部署相關(guān)庫(kù)的安裝的安裝是一個(gè)中支持渲染的工具,本節(jié)來(lái)介紹一下它的安裝方式。另外一個(gè)是的庫(kù)的安裝,安裝之后即可在中使用服務(wù)。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---10、爬蟲框架的安裝:PySpider、Scrapy下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---12、部署相關(guān)庫(kù)的安裝:Docker、Scrapyd Scrap...

    harryhappy 評(píng)論0 收藏0
  • Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---36、分析Ajax爬取今日頭條街拍美圖

    摘要:上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)數(shù)據(jù)爬取下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)動(dòng)態(tài)渲染頁(yè)面抓取本節(jié)我們以今日頭條為例來(lái)嘗試通過(guò)分析請(qǐng)求來(lái)抓取網(wǎng)頁(yè)數(shù)據(jù)的方法,我們這次要抓取的目標(biāo)是今日頭條的街拍美圖,抓取完成之后將每組圖片分文件夾下載到本地保存下來(lái)。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---35、 Ajax數(shù)據(jù)爬取下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---37、動(dòng)態(tài)渲染頁(yè)面抓取:Selenium 本節(jié)我們...

    Leck1e 評(píng)論0 收藏0
  • Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---35、 Ajax數(shù)據(jù)爬取

    摘要:所以說(shuō),我們所看到的微博頁(yè)面的真實(shí)數(shù)據(jù)并不是最原始的頁(yè)面返回的,而是后來(lái)執(zhí)行后再次向后臺(tái)發(fā)送了請(qǐng)求,拿到數(shù)據(jù)后再進(jìn)一步渲染出來(lái)的。結(jié)果提取仍然是拿微博為例,我們接下來(lái)用來(lái)模擬這些請(qǐng)求,把馬云發(fā)過(guò)的微博爬取下來(lái)。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---34、數(shù)據(jù)存儲(chǔ):非關(guān)系型數(shù)據(jù)庫(kù)存儲(chǔ):Redis下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---36、分析Ajax爬取今日頭條街拍美圖 ...

    Thanatos 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<