第七部分(二) 動態渲染頁面爬取(Splash的安裝和使用、Scrapy的安裝、Docker的安裝、Scrapy-Splash的安裝,在Windows及Linux平台的安裝)


二、 Splash的使用

Splash是一個JavaSscript渲染服務,一個帶有HTTP API的輕量級瀏覽器,同時對接了Python中的Twisted和QT庫。使用它同樣可以實現動態渲頁面的抓取。

Splash可以實現下面的這些功能:
異步方式處理多個網頁渲染過程;
獲取渲染后的頁面的源代碼或截圖;
通過關閉圖片渲染或者使用Adblock規則來加快頁面渲染速度;
可執行特定的 JavaScript 腳本;
可通過 Lua 腳本來控制頁面渲染過程;
獲取渲染的詳細過程並通過HAR(HTTP Archive)格式呈現。


在使用之前先進行Scrapy及Scrapy-Splash的安裝。

1、 Scrapy的安裝
Scrapy是非常強大的爬蟲框架,依賴的庫較多,至少要依賴的庫有 Twisted 14.0 、lxml 3.4 和 pyQpenSSL 0.14。不同的平台依賴的庫也不相同,在安裝前確保一些基本庫已經正確安裝好。

對於Windows,Python是使用Anaconda安裝的,可使用conda命令安裝Scrapy,如果不是,需要一步步安裝Scrapy。
conda install Scrapy

CentOS7安裝 Scrapy:
首先確保依賴庫已經安裝,運行如下命令:
yum groupinstall -y development tools
yum install -y epel-release libxslt-devel libxml2-devel openssl-devel

沒有報錯,接着用 pip 安裝 Scrapy:
pip3 install Scrapy
安裝的時候報下面這個錯誤:
No matching distribution found for Twisted>=13.1.0 (from Scrapy)
下載Twisted這個安裝包:
wget https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2
tar -xjvf Twisted-18.9.0.tar.bz2
cd Twisted-18.9.0/
python3 setup.py install
執行這條命令報下面的錯誤:
src/twisted/test/raiser.c:4:20: 致命錯誤:Python.h:沒有那個文件或目錄
解決方法,安裝 python-dev:
yum install python-devel
再次執行安裝命令,安裝Twisted成功:
python3 setup.py install
接下來再一次用 pip 命令安裝 Scrapy,這次提示安裝成功:
pip3 install Scrapy

安裝完成后在命令行執行 scrapy 后可以看到可用命令選項。

下面是一些與scrapy相關的網站:
scrapy官方網站:https://scrapy.org
scrapy官方文檔:https://docs.scrapy.org
scrapy PyPI:https://pypi.python.org/pypi/Scrapy
scrapy GitHub:https://github.com/scrapy/scrapy
scrapy 中文文檔:https://scrapy-chs.readthedocs.io/zh_CN/0.24/
python wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

2、 安裝Docker
在安裝Splash之前,還需要安裝Docker才行。Docker是一種部署方式,叫作Docker集群部署。可將爬蟲制作為Docker鏡像,在主機上安裝了Docker,就可直接運行爬蟲,不用擔心環境配置、版本問題。

Docker是一種容器技術,可以將應用和環境等進行打包,形成一個獨立的、類似於iOS 的App形式的“應用” 。這個應用可以直接被分發到任意一個支持Docker 的環境中, 通過簡單的命令即可啟動運行。Docker 是一種最流行的容器化實現方案,和虛擬化技術類似,它極大地方便了應用服務的部署;又與虛擬化技術不同,它以一種更輕量的方式實現了應用服務的打包。使用Docker ,可以讓每個應用彼此相互隔離,在同一台機器上同時運行多個應用,不過它們彼此之間共享同一個操作系統。Docker 的優勢在於,它可以在更細的粒度上進行資源管理,也比虛擬化技術更加節約資源。

用Docker大規模部署爬蟲系統會大提高效率。所以需要先安裝Docker容器。
與Docker相關的網站如下:
官方網站:https://www.docker.com
GitHub:https://github.com/docker
Docker Hub: https://hub.docker.com
官方文檔:https://docs.docker.com
DaoCloud: http://www.daocloud.io
中文社區:http://www.docker.org.cn
中文教程:http://www.runoob.com/docker/docker-tutorial.html
推薦書籍:https://yeasy.gitbooks.io/docker_practice

windows 10系統安裝Docker,在官方網站上下載 Docker for Windows安裝包進行安裝。安裝完成后在命令行輸入 docker 會看到命令選項信息,表示安裝成功。

CentOS7 安裝Docker,使用下面的一條命令即可完成,安裝過程較慢:
curl -sSL https://get.docker.com/ | sh

3、 Scrapy-Splash的安裝
Scrapy-Splash是一個Scrapy中支持JavaScript渲染的工具。安裝分為兩部分。一個是Splash服務的安裝,通過Docker安裝,安裝后會啟動一個Splash服務,可通過它的接口來實現JavaScript頁面的加載。另一個是Scrapy-Splash的Python庫的安裝,安裝后可在Scrapy中使用Splash服務。

與Scrapy-Splash相關的連接如下:
Github: https://github.com/scrapy-plugins/scrapy-splash
PyPI: https://pypi.python.org/pypi/scrapy-splash
使用說明:https://github.com/scrapy-plugins/scrapy-splash#configuration
Splash官方文檔:http://splash.readthedocs.io

安裝splash,Scrapy-Splash要使用HTTP API進行頁面渲染,所以需要安裝Splash來提供渲染服務,通過docker安裝,在用docker安裝前,需要在命令行先登錄docker,登錄命令是:
docker login,根據提示輸入用戶名和密碼,用戶名和密碼是官方網站上注冊的用戶名和密碼,注意用戶名不是注冊時的郵箱。
windows下登錄成功后執行安裝命令如下:
docker run -p 8050:8050 scrapinghub/splash

安裝完成后可以看到Splash已經在8050端口上運行的輸出結果,這時打開 http://localhost:8050/ 可以看到Splash的主面。如圖2-1所示。
圖2-1 Splash運行主頁面
圖2-1 Splash運行主頁面

Splash可以直接安裝在運程服務器上,在運程服務器上以守護態運行Splash即可,這樣在中斷與運程服務器連接后,不會終止Splash服務的運行。命令如下所示,命令中的 -d 參數表示將Docker容器以守護態運行。
docker run -d -p 8050:8050 scrapinghub/splash

3.1、 在CentOS7安裝Scrapy-Splash,提示下面錯誤
docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?.
解決辦法:
systemctl daemon-reload
systemctl restart docker
service docker status # 查看運行情況
systemctl status docker.service # 執行這個命令也是查看運行情況

配置鏡像加速器,加速文件是在 /etc/docker/daemon.json,如果沒有就創建該文件,執行下面這條命令:
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
重啟docker:
systemctl restart docker
運行安裝命令:
docker run -p 8050:8050 scrapinghub/splash
執行完成安裝命令后,在 windows 的瀏覽器地址欄可以正常訪問 http://192.168.64.50:8050,此時可以正常使用 splash。

不同平台配置鏡像加速方法參考:
http://guide.daocloud.io/dcs/daocloud-9153151.html

4、 Scrapy-Splash的安裝
安裝完Splash后,接下來就安裝其對應的Python庫,使用pip命令進行安裝,如下所示:
pip3 install scrapy-splash

5、 Splash簡單使用
在圖2-1的Splash主頁面中,右上方有的輸入框中輸入 https://www.baidu.com,點擊 Render me開始渲染,如果如圖2-2所示。
圖2-2 Splash渲染百度頁面
圖2-2 Splash渲染百度頁面

可以看出,網頁的返回結果呈現了渲染圖、HAR加載統計數據、網頁源代碼。在HAR結果中可以看到,Splash執行了整個網頁的渲染過程,包括CSS、JavaScript的加載等過程,呈現的頁面與在瀏覽器中得到的結果是一樣的。這個過程是由圖2-1中Splash運行主頁面中的一段Lua語言寫的腳本控制的。代碼如下:
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

這段Lua語言腳本的意思是:
splash:go(args.url):先調用go()方法加載頁面;
splash:wait(0.5):再調用wait()方法等待一定時間
return返回源代碼、截圖和HAR信息。

通過上面了解到,Splash通過Lua腳本控制頁面的加載過程,加載過程完全模擬瀏覽器,最后返回各種樣式的結果。

6、 Splash Lua腳本
用Splash來模擬類似Chrome、PhangtomJS的操作。先了解下Splash Lua腳本的入口和執行方式。

6.1、 Lua入口及返回值
看下面這個簡單的Lua腳本:
function main(splash, args)
splash:go("https://www.baidu.com")
splash:wait(0.5)
local title=splash:evaljs("document.title")
return {title=title}
end

將段代碼粘貼到圖2-1中的代碼編輯區域后點擊Render me! 進行測試。結果是返回網頁的標題。代碼中 evaljs() 方法中傳入的是JavaScript腳本,document.title返回的是網頁標題,接着將這個結果賦值給一個title變量,隨后將其返回。

代碼定義的方法名稱叫作main(),這個名稱是固定的,Splash默認會調用這個方法。該方法的返回值可以是字典形式,也可以是字符串形式,最后都會轉化為 Splash HTTP Response,例如下面這樣:
function main(splash)
return {hello="python"} # 返回字典形式的內容
end
function main(splash)
return "python" # 返回字符串形式的內容
end

6.2、 異步處理
Splash支持異步處理,但是這里沒有顯式指明回調方法,其回調的跳轉是在Splash內部完成的。示例如下:
function main(splash, args)
local example_urls = {"www.baidu.com", "www.taobao.com", "www.zhihu.com"}
local urls = args.urls or example_urls
local results = {}
for index, url in ipairs(urls) do
local ok, reason = splash:go("https://" .. url)
if ok then
splash:wait(2)
results[url] = splash:png()
end
end
return results
end

運行這段代碼后返回的是3個站點的截圖。代碼中的wait()方法相當於Python中的sleep(),參數是等待秒數。在splash執行到此方法時,它會轉而去處理其他任務,然后在指定的時間過后再回來繼續處理。要注意的是,Lua腳本中字符串拼接和Python不同,它使用的是(..)操作符,而不是(+)。這里在加載時做了異常檢測。go()方法會返回加載頁面的結果狀態,如果頁面出現 4xx 或 5xx 狀態碼,ok 變量就為空,就不會返回加載后的圖片。Lua 腳本語法參考:
http://www.runoob.com/lua/lua-basic-syntax.html

7、 Splash對象屬性
前面的Lua代碼中,main()方法的第一個參數是splash,這個對象非常重要,類似於Selenium中的WebDriver對象,可以調用它的一些屬性和方法來控制加載過程。下面來看下它的屬性。

7.1、 args屬性
獲取加載時配置的參數,如URL,如果為GET請求,可獲取GET請求參數;如果為POST請求,可獲取表單提交的數據。Splash也支持使用第二個參數直接作為args,例如這樣:

function main(splash, args)
local url = args.url
end
第二個參數 args 相當於 splash.args屬性,上面代碼等價於這樣:
function main(splash, args)
local url = splash.args.url
end

7.2、 js_enabled屬性
Splash的JavaScript執行開關,可設置為true或false來控制是否執行JavaScript代碼,默認是true。如果設置為false時,調用evaljs()方法執行JavaScript時就會拋出異常。如下所示:
function main(splash, args)
splash:go("https://www.baidu.com")
splash.js_enabled=false
local title=splash:evaljs("document.title")
return {title=title}
end

執行這段代碼,拋出下面的錯誤提示信息:
{
"description": "Error happened while executing Lua script",
"type": "ScriptError",
"info": {
"source": "[string \"function main(splash, args)\r...\"]",
"js_error_message": null,
"splash_method": "evaljs",
"line_number": 4,
"message": "[string \"function main(splash, args)\r...\"]:4: unknown JS error: None",
"type": "JS_ERROR",
"error": "unknown JS error: None"
},
"error": 400
}

7.3、 resource_timeout屬性
設置加載的超時時間,單位是秒。設置為 0 或 nil (相當於Python中的None)表示不檢測超時。這個屬性適合用在加載較慢的網頁,如果超過某個時間無響應,忽略異常即可。示例如下:

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

執行代碼后的錯誤信息如下所示:
{
"description": "Error happened while executing Lua script",
"type": "ScriptError",
"info": {
"line_number": 3,
"source": "[string \"function main(splash)\r...\"]",
"type": "LUA_ERROR",
"message": "Lua error: [string \"function main(splash)\r...\"]:3: network5",
"error": "network5"
},
"error": 400
}

7.4、 images_ enabled屬性
設置圖片是否可加載,默認是可加載。禁用該屬性可節省網絡流量並提高網頁加載速度。要注意的是,禁用圖片加載可能會影響JavaScript渲染,因為外層DOM節點的高度會受影響,進而影響DOM節點的位置。因此,JavaScript對圖片節點有操作的話,其執行就會受到影響。

另外要注意的是,Splash使用了緩存。如果一開始加載出來了圖片,然后禁用圖片加載,再重新加載頁面,之前加載好的圖片可能還會顯示出來,這時重啟Splash即可。示例如下:

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

7.5、 plugins _enabled屬性
控制瀏覽器插件(如Flash插件)是否開啟,默認是false,不開啟。可使用下面方式開啟或關閉該屬性:
splash.plugins_enabled = true/false

7.6、 scroll_position屬性
設置此屬性可控制頁面上下或左右滾動。是一個常用的屬性,示例如下:
function main(splash, args)
assert(splash:go('https://www.taobao.com'))
splash.scroll_position = { y=400 }
return { png=splash:png() }
end

代碼中 y=400 控制頁面向下滾動400像素值。要控制左右滾動,可傳入 x 參數,如下所示:
splash.scroll_position = {x=100, y=200}

8、 Splash對象的方法
Splash對象有下面這些方法。

8.1、 go()方法
請求某個鏈接,可模擬GET和POST請求,支持傳入請求頭、表單等數據,其用法如下:
ok, reason = splash:go{url, baseurl=nil, headers=nil, http_method="GET", body=nil, formdata=nil}
go()方法參數說明:
url: 請求的URL。
baseurl: 可選參數,默認為空,表示資源加載相對路徑。
headers: 可選參數,默認為空,表示請求頭。
http_method: 可選參數,默認為GET,同時支持POST。
body: 可選參數,默認為空,發POST請求時的表單數據,使用的Content-type為application/json
formdata: 可選參數,默認為空,POST的時候的表單數據,使用的Content-type為application/x-www-form-urlencode。

該方法返回結果是結果ok和原因reason的組合,如果ok為空,代表網頁加載出現錯誤,此時reason變量中包含了錯誤的原因。否則表示頁面加載成功。示例如下:

function main(splash, args)
local ok, reason = splash:go{"http://httpbin.org/post", http_method="POST", body="name=Michael"}
if ok then
return splash:html()
end
end

這段代碼模擬POST請求,並傳入POST的表單數據,如果成功,則返回頁面源代碼。運行結果如下所示,在下面結果中有"name": "Michael",表示成功實現了POST請求並發送表單數據。
<html><head></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">{
"args": {},
"data": "",
"files": {},
"form": {
"name": "Michael"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en,*",
"Content-Length": "12",
"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": "183.221.39.12, 183.221.39.12",
"url": "https://httpbin.org/post"
}
</pre></body></html>

8.2、 wait()方法
控制頁面的等待時間,用法如下:
ok, reason = splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}
wait()方法參數說明:
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

8.3、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.taobao.com")
return ("There are %s DIVs"):format(get_div_count())
end
運行結果如下:
There are 562 DIVs

這段代碼中,先聲明一個JavaScript方法,在頁面加載成功后調用此方法計算頁面中的div節點個數。有關 JavaScript 到 Lua 腳本的轉換細節,參考官方文檔:
https://splash.readthddocs.io/en/stable/scripting-ref.html#splash-jsfunc

8.4、 evaljs()方法
可執行JavaScript代碼並返回最后一條JavaScript語句的返回結果,用法如下:
result = splash:evaljs(js)
比如用來獲取標題的方法:
local title = splash:evaljs("document.title")

8.5、 runjs()方法
該方法可執行JavaScript代碼,它與evaljs()的功能類似,但是更偏向於執行某些動作或聲明某些方法。例如:
function main(splash, args)
splash:go("https://www.baidu.com")
assert(splash:runjs("foo = function (){ return 'michael' }"))
local result = splash:evaljs("foo()")
return result
end

這里用runjs()先聲明一個JavaScript定義的方法,然后通過evaljs()來調用得到的結果。輸出是:michael

8.5、 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.zhihu.com")
return splash:evaljs("get_document_title()")
end

這段代碼中調用了autoload()方法聲明一個JavaScript方法,之后通過 evaljs() 方法執行此 JavaScript 方法,運行結果是:
知乎 - 有問題,上知乎

此外,還可用 autoload() 方法加載某些庫,如 jQuery,示例如下:
function main(splash, args)
assert(splash:autoload("https://code.jquery.com/jquery-1.12.4.min.js"))
assert(splash:go("https://www.sina.com.cn"))
local version = splash:evaljs("$.fn.jquery")
return 'JQuery version:' .. version
end

運行結果是:JQuery version:1.7.2,這個版本號是新浪網站的。

8.6、 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)
return snapshots
end

這段代碼中設置了一個定時任務,0.2 秒時獲取網頁截圖,然后等待1秒,即 1.2秒時再次獲取網頁截圖,訪問的是淘寶首頁。

8.7、 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 343)
{
"args": {},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en,*",
"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": "183.221.39.12, 183.221.39.12",
"url": "https://httpbin.org/get"
}
status: 200
url: "http://httpbin.org/get"

8.8、 http_post()方法
模擬發送POST請求,比http_get()方法多一個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({job="michael"}),
headers={["content-type"]="application/json"}
}
return {
html=treat.as_string(response.body),
url=response.url,
status=response.status
}
end

運行結果如下所示,從輸出可看出,成功模擬提交了POST請求並發送了表單數據(body數據):
Splash Response: Object
html: String (length 521)
{
"args": {},
"data": "{\"job\": \"michael\"}",
"files": {},
"form": {},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en,*",
"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": {
"job": "michael"
},
"origin": "183.221.39.12, 183.221.39.12",
"url": "https://httpbin.org/post"
}
status: 200
url: "http://httpbin.org/post"

8.9、 set_content()方法
設置頁面的內容,用法示例如下所示:
function main(splash)
assert(splash:set_content("<html><body><h1>helle world!</h1></body></html>"))
return splash:png()
end

運行這段代碼,輸出是含有 helle world! 字樣的頁面截圖。

8.10、 html()方法
獲取網頁小源代碼,即簡單又常用的方法。
function main(splash, args)
splash:go("http://httpbin.org.get")
return splash:html()
end

8.11、 png()方法
獲取PNG格式的網頁截圖,示例如下:
function main(splash, args)
splash:go("https://www.taobao.com")
return splash:png()
end

8.12、 jpeg()方法
獲取JPEG格式的網頁截圖,示例如下:
function main(splash, args)
splash:go("https://www.taobao.com")
return splash:jpeg()
end

8.13、 har()方法
獲取頁面加載過程描述,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:har()
end

運行結果會顯示頁面加載過程中每個請求記錄的詳情。

8.13、 url()方法
獲取當前正在訪問的URL,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:url()
end
運行結果是:https://www.baidu.com/

8.14、 get_cookies()方法
獲取當前頁面的Cookies,示例如下:
function main(splash, args)
splash:go("https://www.baidu.com")
return splash:get_cookies()
end
運行結果大致內容如下所示:
Splash Response: Array[7]
0: Object
domain: ".baidu.com"
expires: "2087-04-12T05:03:05Z"
httpOnly: false
name: "BAIDUID"
path: "/"
secure: false
value: "FFA9DF2148D3AED2188D59DFC19D6733:FG=1"
1: Object
domain: ".baidu.com"
expires: "2087-04-12T05:03:05Z"
httpOnly: false
name: "BIDUPSID"
path: "/"
secure: false
value: "FFA9DF2148D3AED2188D59DFC19D6733"
2: Object
domain: ".baidu.com"
expires: "2087-04-12T05:03:05Z"
httpOnly: false
name: "PSTM"
path: "/"
secure: false
value: "1553478538"

8.15、 add_cookie()方法
為當前頁面添加Cookie,使用方法如下:
cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}
各個參數代碼Cookie的各個屬性。
function main(splash, args)
splash:add_cookie{"sessioned", "12345michael", "/", domain="http://example.com"}
splash:go("http://example.com/")
return {splash:html(), splash:get_cookies()}
end

8.16、 clear_ cookies()方法
清除所有的Cookies,示例如下:
function main(splash)
splash:go("https://www.baidu.com/")
splash:clear_cookies()
return splash:get_cookies()
end
代碼中先用clear_cookies()方法清除cookies,再用get_cookies()方法獲取,此時獲取到cookies為空的。

8.17、 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

8.18、 set_viewport_size()方法
設置當前瀏覽器頁面的大小,即寬高,用法如下:
splash:set_viewport_size(sidth, height)
需要訪問一個寬度自適應的頁面才能看到效果:
function main(splash)
splash:set_viewport_size(400, 700)
assert(splash:go("https://www.baidu.com"))
return splash:png()
end

8.19、 set_viewport_full()方法
設置瀏覽器全屏顯示,示例如下:
function main(splash)
splash:set_viewport_full()
assert(splash:go("https://www.baidu.com"))
return splash:png()
end

8.20、 set_user_agent()方法
設置瀏覽器的User_Agent,示例如下:
function main(splash)
splash:set_agent('Splash')
splash:go("http://httpbin.org/get")
return splash:html()
end
將瀏覽器的User-Agent設置為Splash,運行結果中有:"User-Agent": "Splash"。可以看到User-Agent被成功設置。

8.21、 set_custom_headers()方法
設置請求頭,示例如下:
function main(splash)
splash: set_custom_headers({
["User-Agent"] = "Splash",
["Site"] = "Splash",
})
splash:go("http://httpbin.org/get")
return splash:html()
end
代碼中設置了請求頭中的User-Agent 和 site 屬性,運行結果中有:"Site": "Splash", "User-Agent": "Splash",表示設置
成功。

8.22、 select()方法
該方法可以選中符合條件的第一個節點,如果有多個節點符合條件,則只會返回一個,其參數是CSS選擇器。示例如下:
function main(splash)
splash:go("https://www.baidu.com")
cfz = splash:select("#kw")
cfz:send_text("Python")
splash:wait(1)
return splash:png()
end
代碼中go()方法訪問百度,選中搜索框后調用 send_text() 方法填入關鍵字后返回網頁截圖。

8.23、 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

代碼中用CSS選擇器選中了節點的正文內容,隨后遍歷所有節點,將將其中文本獲取下來。運行結果省略。

8.24、 mouse_click()方法
模擬鼠標點擊操作,傳入的參數為坐標值 x 和 y。也可以直接選中某個節點,然后調用此方法,示例如下:
function main(splash)
splash:go("https://www.baidu.com")
input = splash:select("#kw") --獲取輸入框
input: send_text("Splash") --輸入文本Splash
submit = splash:select("#su") --獲取點擊按鈕
submit:mouse_click() --點擊
splash:wait(3)
return splash:png()
end

在這段代碼中,選中頁面的輸入框,輸入文本,然后選中“提交”按鈕,接着調用 mouse_click() 方法提交查詢,並返回截圖。運行結果省略。

更詳細的 Splash API操作,Splash 對象的所有API操作,參考官方文檔:
https://splash.readthedocs.io/en/stable/scripting-ref.html
針對頁面元素的 API 操作,參考下面這個官方文檔:
https://splash.readthedocs.io/en/stable/scripting-element-object.html

9、 Splash API調用
Splash提供的一些 HTTP API 接口,可利用Splash渲染頁面,並與Python程序結合使用抓取 JavaScript 渲染的頁面。調用這些接口傳入相應的參數即可。

9.1、 render.html接口
用於獲取 JavaScript 渲染的頁面的HTML代碼,接口地址是Splash 的運行地址加此接口名稱,例如 http://localhost:8050/render.html。
可用 curl 進行測試,在命令行下輸入下面的代碼:
curl http://localhost:8050/render.html?url=https://www.baidu.com
這行代碼給此接口傳遞一個 url 參數來指定渲染的 URL,返回結果即頁面渲染后的源代碼。用Python來實現,代碼如下:
import requests
url = "http://localhost:8050/render.html?url=https://www.baidu.com"
response = requests.get(url)
print(response.text)
通過這幾行代碼就可輸出百度頁面渲染后的源代碼。

此接口還可以指定其它參數,如通過 wait 參數指定等待秒數,在需要確保頁面完全加載出來時,可增加等待時間。例如:
import requests
url = "http://localhost:8050/render.html?url=https://www.taobao.com&wait=5"
response = requests.get(url)
print(response.text)
這樣的請求時間會變長,這里是等待5秒過后獲取淘寶的源代碼。注意參數是以 &wait=5 這種方式出現在 URL 中。

此外,接口還支持代理設置、圖片加載設置、Headers 設置、請求方法設置,詳細情況參考官方文檔:
https://splash.readthedocs.io/en/stable/api.html#render-html

9.2、 render.png接口
獲取網頁截圖,需要指定圖形的大小,可通過 width 和 height 來控制寬高,返回的是 PNG 格式的圖片二進制數據。示例如下:
curl http://localhost:8050/render.png?url=https://www.taobao.com&wait=3&width=1000&height=700

這行代碼中使用 render.pgn 接口,設置了等待參數 wait ,另外通過 width 和 height 設置了頁面大小。

利用 Python 實現,可將返回的二進制數據保存為PNG格式的圖片,如下面方法所示:
import requests
url = "http://localhost:8050/render.png?url=https://www.taobao.com&wait=3&width=1000&height=700"
response = requests.get(url)
with open('taobao.png', 'wb') as f:
f.write(response.content)
通過wait參數設置頁面渲染的程度,並且返回渲染后的截圖。

更多詳細參數設置參考官方文檔:
https://splash.readthedocs.io/en/stable/api.html#render-png

9.3、 render.jpeg接口
與 render.png 接口類似,返回的是 JPEG 格式的圖片二進制數據。該接口多了 quality 參數設置圖片質量。

9.4、 render.har接口
獲取頁面加載的 HAR 數據,用法如下:
curl http://localhost:8050/render.har?url=https://www.jd.com&wait=5
該行代碼返回結果非常多,是一個 JSON 格式的數據,其中包含頁面加載過程中的 HAR 數據。

9.5、 render.json接口
該接口包含了前面接口的所有功能,返回結果是 JSON 格式,用法如下:
curl http://localhost:8050/render.json?url=https://www.baidu.com
輸出結果如下所示:
{'requestedUrl': 'https://www.baidu.com/', 'geometry': [0, 0, 1024, 768], 'url': 'https://www.baidu.com/', 'title': '百度一下,你就知道'}

從輸出可知,這里以 JSON 形式返回相應請求數據。還可以傳入不同的參數控制其返回結果。比如傳入 html=1,返回結果會增加源代碼數據;傳入 png=1,返回結果會增加頁面 PNG 截圖數據;傳入 har=1,會獲得頁面 HAR 數據。例如:
curl http://localhost:8050/render.json?url=https://www.baidu.com&html=1&har=1
這種方式返回的結果會包含網頁源代碼和 HAR 數據。

更多參數設置,參考官方文檔:
https://splash.readthedocs.io/en/stable/api.html#render-json

9.6、 execute接口
這個是最重要也是最強大的接口,前面的很多 Splash Lua 腳本的操作,用此接口便可實現與 Lua 腳本的對接。render.html 和render.png 等這些接口對於一般的 JavaScript 渲染頁面可滿足要求,但要實現一些交互操作就無能為力,這時就需要使用 execute接口。先寫一個簡單的 Lua 腳本,直接返回數據:
function main(splash)
return 'hello python'
end

接下來將此腳本轉化為 URL 編碼后的字符串(在頁面上執行這段腳本可進行轉化),拼接到 execute 接口后面,如下所示:
curl http://localhost:8050/execute?lua_source=function+main%28splash%29%0D%0A++++return+%27hello+python%27%0D%0Aend
運行結果輸出: hello python

通過 lua_source 參數傳遞轉碼后的 Lua 腳本,通過 execute 接口獲取了最終腳本的執行結果。上面這些操作可用 Python 來實現,代碼如下:

import requests
from urllib.parse import quote
lua = '''
function main(splash)
return 'hello python'
end
'''
url = 'http://localhost:8050/execute?lua_source=' + quote(lua)
response = requests.get(url)
print(response.text)

運行結果輸出為:hello python
這段代碼中用三引號將 Lua 腳本包括起來,用 urllib.parse 模塊的 quote() 方法將腳本進行 URL 轉碼,隨后構造 Splash 請求 URL,將其作為 lua_source 參數傳遞,這樣運行結果就會顯示 Lua 腳本執行后的結果。再來看另一個實例:

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)

輸出結果如下所示:
{"html": "{\n \"args\": {}, \n \"headers\": {\n \"Accept-Encoding\": \"gzip, deflate\", \n
\"Accept-Language\": \"en,*\", \n \"Host\": \"httpbin.org\", \n
\"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1\"\n }, \n
\"origin\": \"183.221.39.19, 183.221.39.19\", \n \"url\": \"https://httpbin.org/get\"\n}\n",
"status": 200, "url": "http://httpbin.org/get"}
從輸出可知,返回結果是 JSON 形式,成功獲取了請求的 URL、狀態碼和網頁源代碼。通過這種方式,可將 Lua 腳本與 Python 進行對接,所有網頁的動態渲染、模擬點擊、表單提交、頁面滑動、延時等待后的一些結果均可自由控制,獲取頁面源碼和截圖都能實現。

利用 Python 和 Splash 可以實現 Javascript渲染的頁面抓取,同時有強大的渲染功能,並且不需要瀏覽器即可渲染,使用很方便,不像 Selenium 需要使用瀏覽器。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM