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

資訊專欄INFORMATION COLUMN

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

姘擱『 / 3566人閱讀

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

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

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

1. 功能介紹

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

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

獲取渲染后的頁面的源代碼或截圖

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

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

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

接下來我們來了解一下它的具體用法。

2. 準(zhǔn)備工作

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

3. 實(shí)例引入

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

圖 7-6 Web 頁面
在右側(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)頁的返回結(jié)果呈現(xiàn)了渲染截圖、HAR 加載統(tǒng)計(jì)數(shù)據(jù)、網(wǎng)頁的源代碼。
通過 HAR 的結(jié)果我們可以看到 Splash 執(zhí)行了整個(gè)網(wǎng)頁的渲染過程,包括 CSS、JavaScript 的加載等過程,呈現(xiàn)的頁面和我們?cè)跒g覽器得到的結(jié)果完全一致。
那么這個(gè)過程是由什么來控制的呢?我們重新返回首頁可以看到實(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 語言寫的腳本,Lua也是一門編程語言,簡(jiǎn)潔易用。
即使我們不懂這個(gè)語言的語法,但通過腳本表面意思我們也可以大致了解到它是首先調(diào)用 go() 方法去加載頁面,然后調(diào)用 wait() 方法等待了一定的時(shí)間,最后返回了頁面的源碼、截圖和 HAR 信息。
所以到這里我們可以大體了解到 Splash 是通過 Lua 腳本來控制了頁面的加載過程,加載過程完全模擬瀏覽器,最后可返回各種格式的結(jié)果,如網(wǎng)頁源碼、截圖等。
所以接下來我們要學(xué)會(huì)用 Splash 的話,一是需要了解其中 Lua 腳本的寫法,二是需要了解相關(guān) API 的用法,那么接下來我們就來了解一下這兩部分內(nèi)容。

4. Splash Lua腳本

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

入口及返回值

首先我們來看一個(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)擊按鈕來測(cè)試一下。
這樣我們就會(huì)看到其返回了網(wǎng)頁的標(biāo)題,這里我們是通過 evaljs() 方法傳入 JavaScript 腳本,而 document.title 的執(zhí)行結(jié)果就是返回網(wǎng)頁標(biāo)題,執(zhí)行完畢后賦值給一個(gè) title 變量,隨后將其返回,這樣就可以看到其返回結(jié)果就是網(wǎng)頁標(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是支持異步處理的,但是這里我們并沒有顯式地指明回調(diào)方法,其回調(diào)的跳轉(zhuǎn)是在Splash內(nè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í)間過后再回來繼續(xù)處理。
在這里值得注意的是 Lua 腳本中的字符串拼接,和 Python不同,這里的字符串拼接使用的是 .. 操作符,而不是 +,如有必要可以簡(jiǎn)單了解一下Lua腳本的語法,鏈接:http://www.runoob.com/lua/lua...。
另外這里我們做了加載時(shí)的異常檢測(cè),go() 方法會(huì)返回加載頁面的結(jié)果狀態(tài),如果頁面出現(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)用它的一些屬性和方法來控制加載過程,接下來我們首先看下它的屬性。

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 來控制是否可以執(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"
}

不過一般來說我們不用設(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)沒有得到響應(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)頁加載速度較慢的情況下設(shè)置,如果超過了某個(gè)時(shí)間無響應(yīng)則直接拋出異常并忽略即可。

images_enabled

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

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

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

plugins_enabled

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

splash.plugins_enabled = true/false
scroll_position

此屬性可以控制頁面的滾動(dòng)偏移,通過設(shè)置此屬性我們可以控制頁面上下或左右滾動(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

這樣我們就可以控制頁面向下滾動(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() 方法就是用來請(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ù)說明如下:

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)頁加載出現(xiàn)了錯(cuò)誤,此時(shí) reason 變量中包含了錯(cuò)誤的原因,否則證明頁面加載成功,示例如下:

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

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

wait()

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

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

參數(shù)說明如下:

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)訪問淘寶并等待 2 秒,隨后返回頁面源代碼的功能。

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

evaljs()

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

result = splash:evaljs(js)

比如我們可以用下面的代碼來獲取頁面的標(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 定義的方法,然后通過 evaljs() 來調(diào)用得到結(jié)果。
運(yùn)行結(jié)果如下:

bar
autoload()

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

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

參數(shù)說明如下:

source_or_url,JavaScript 代碼或者 JavaScript 庫鏈接。

source,JavaScript 代碼。

url,JavaScript 庫鏈接

但是此方法只負(fù)責(zé)加載 JavaScript 代碼或庫,不執(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 方法,然后通過 evaljs() 調(diào)用了此方法執(zhí)行。
運(yùn)行結(jié)果:

百度一下,你就知道

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

此方法可以通過設(shè)置定時(shí)任務(wù)和延遲時(shí)間實(shí)現(xiàn)任務(wù)延時(shí)執(zhí)行,并且可以在執(zhí)行前通過 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)頁截圖,然后等待 1 秒,1.2 秒時(shí)再次獲取網(wǎng)頁截圖,訪問的頁面是淘寶,最后將截圖結(jié)果返回。
運(yùn)行結(jié)果如圖 7-11 所示:

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

http_get()

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

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

參數(shù)說明如下:

url,請(qǐng)求URL。

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

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

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)求,不過多了一個(gè)參數(shù) body,使用方法如下:

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

參數(shù)說明如下:

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

此方法可以用來設(shè)置頁面的內(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()

此方法可以用來獲取網(wǎng)頁的源代碼,非常簡(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()

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

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

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

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

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

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

url()

此方法可以獲取當(dāng)前正在訪問的 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)前頁面的 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)前頁面添加 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被全部清空,沒有任何結(jié)果。

get_viewport_size()

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

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)前瀏覽器頁面的大小,即寬高,用法如下:

splash:set_viewport_size(width, height)

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

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

在這里我們首先訪問了百度,然后選中了搜索框,隨后調(diào)用了 send_text() 方法填寫了文本,然后返回網(wǎng)頁截圖。
結(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

在這里我們通過 CSS 選擇器選中了節(jié)點(diǎn)的正文內(nèi)容,隨后遍歷了所有節(jié)點(diǎn),然后將其中的文本獲取了下來。
運(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)容獲取了下來。

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

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

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

7. Splash API調(diào)用

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

render.html

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

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

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

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

這樣我們就可以成功輸出百度頁面渲染后的源代碼了。
另外此接口還可以指定其他參數(shù),如 wait 指定等待秒數(shù),如果我們要確保頁面完全加載出來可以增加等待時(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 渲染后的淘寶頁面源代碼。
另外此接口還支持代理設(shè)置、圖片加載設(shè)置、Headers設(shè)置、請(qǐng)求方法設(shè)置,具體的用法可以參見官方文檔:https://splash.readthedocs.io...。

render.png

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

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

在這里我們還傳入了 width 和 height 來放縮頁面大小為 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é)果
這樣我們就成功獲取了京東首頁渲染完成后的頁面截圖,詳細(xì)的參數(shù)設(shè)置可以參考官網(wǎng)文檔:https://splash.readthedocs.io...。

render.jpeg

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

render.har

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

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

返回結(jié)果非常多,是一個(gè) Json 格式的數(shù)據(jù),里面包含了頁面加載過程中的 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ù)。
我們可以通過傳入不同的參數(shù)控制其返回的結(jié)果,如傳入html=1,返回結(jié)果即會(huì)增加源代碼數(shù)據(jù),傳入 png=1,返回結(jié)果機(jī)會(huì)增加頁面 PNG 截圖數(shù)據(jù),傳入har=1則會(huì)獲得頁面 HAR 數(shù)據(jù),例如:

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

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

execute

此接口才是最為強(qiáng)大的接口,我們?cè)谇懊嬲f了很多 Splash Lua 腳本的操作,用此接口便可以實(shí)現(xiàn)和 Lua 腳本的對(duì)接。
前面的 render.html、render.png 等接口對(duì)于一般的 JavaScript 渲染頁面是足夠了,但是如果要實(shí)現(xiàn)一些交互操作的話還是無能為力的,所以這里就需要使用此 execute 接口來對(duì)接 Lua 腳本和網(wǎng)頁進(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

在這里我們通過 lua_source 參數(shù)傳遞了轉(zhuǎn)碼后的 Lua 腳本,通過 execute 接口獲取了最終腳本的執(zhí)行結(jié)果。
那么在這里我們更加關(guān)心的肯定是如何用 Python 來實(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)來將 Lua 腳本包括起來,然后用 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è)賮硪粋€(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)頁源代碼。
如此一來,我們之前所說的 Lua 腳本均可以用此方式與 Python 進(jìn)行對(duì)接,這樣的話,所有網(wǎng)頁的動(dòng)態(tài)渲染、模擬點(diǎn)擊、表單提交、頁面滑動(dòng)、延時(shí)等待后的一些結(jié)果均可以自由控制,獲取頁面源碼和截圖都不在話下。

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

如果我們用 Splash 來做 JavaScript 動(dòng)態(tài)渲染的頁面的抓取的話,如果爬取的量非常大,任務(wù)非常多,如果我們用一個(gè) Splash 服務(wù)來處理的話未免壓力太大了,所以我們可以考慮搭建一個(gè)負(fù)載均衡器來把壓力分散到各個(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ù)完全一致,都是通過 Docker 的 Splash 鏡像開啟的,訪問任何一個(gè)服務(wù)都可以使用 Splash 服務(wù)。

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

接下來我們可以選用任意一臺(tái)帶有公網(wǎng) IP 的主機(jī)來配置負(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;
        }
    }
}

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

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

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),無狀態(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;
}

我們通過 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ù),如用戶登錄后訪問某個(gè)頁面的情形。不過對(duì)于 Splash 來說不需要。

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

sudo nginx -s reload

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

3. 配置認(rèn)證

現(xiàn)在 Splash 是公開訪問的,如果我們不想讓其被公開訪問還可以配置認(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

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

4. 測(cè)試

最后我們可以用代碼來測(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

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

9. 結(jié)語

到現(xiàn)在為止,我們就可以用 Python 和 Splash 實(shí)現(xiàn)JavaScript 渲染的頁面的抓取了,除了 Selenium,本節(jié)所說的 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://specialneedsforspecialkids.com/yun/44107.html

相關(guān)文章

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

    摘要:不過動(dòng)態(tài)渲染的頁面不止這一種。再有淘寶這種頁面,它即使是獲取的數(shù)據(jù),但是其接口含有很多加密參數(shù),我們難以直接找出其規(guī)律,也很難直接分析來抓取。我們用一個(gè)實(shí)例來感受一下在這里們依然是先打開知乎頁面,然后獲取提問按鈕這個(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)渲染頁面抓取:Spla...

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

    摘要:在前面我們講到了和的概念,我們向網(wǎng)站的服務(wù)器發(fā)送一個(gè),返回的的便是網(wǎng)頁源代碼。渲染頁面有時(shí)候我們?cè)谟没蜃ト【W(wǎng)頁時(shí),得到的源代碼實(shí)際和瀏覽器中看到的是不一樣的。所以使用基本請(qǐng)求庫得到的結(jié)果源代碼可能跟瀏覽器中的頁面源代碼不太一樣。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---16、Web網(wǎng)頁基礎(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)庫的安裝的安裝是一個(gè)中支持渲染的工具,本節(jié)來介紹一下它的安裝方式。另外一個(gè)是的庫的安裝,安裝之后即可在中使用服務(wù)。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---10、爬蟲框架的安裝:PySpider、Scrapy下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---12、部署相關(guān)庫的安裝: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)渲染頁面抓取本節(jié)我們以今日頭條為例來嘗試通過分析請(qǐng)求來抓取網(wǎng)頁數(shù)據(jù)的方法,我們這次要抓取的目標(biāo)是今日頭條的街拍美圖,抓取完成之后將每組圖片分文件夾下載到本地保存下來。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---35、 Ajax數(shù)據(jù)爬取下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---37、動(dòng)態(tài)渲染頁面抓取:Selenium 本節(jié)我們...

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

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