1. 程式人生 > >Splash的安裝中出現的問題的解決和基本使用介紹

Splash的安裝中出現的問題的解決和基本使用介紹

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

功能介紹

  • 利用Splash,我們可以實現如下功能:
  • 非同步方式處理多個網頁渲染過程;
  • 獲取渲染後的頁面的原始碼或截圖;
  • 通過關閉圖片渲染或者使用Adblock規則來加快頁面渲染速度;
  • 可執行特定的JavaScript指令碼;
  • 可通過Lua指令碼來控制頁面渲染過程;
  • 獲取渲染的詳細過程並通過HAR(HTTP Archive)格式呈現。

準備工作

我們要進行Splash 的安裝,安裝Splash我們要先安裝Docker,我們只需從

https://docs.docker.com/docker-for-windows/install/下載最新的Docker for Windows安裝包即可,下載過程中我遇到了許多問題下面我就一一幫大家解決

我第一次用docker安裝splash出現這種錯誤,這是因為我們的設定的DOCKER_HOST的設定連線不上去,後來我去百度它給我的解決辦法是輸入【docker-machine env default】,然後我輸了還是不行,還是會繼續報錯,如下所示:

意思就是說Host連線不上去了,這時候我們就要重啟下default,輸入命令【docker-machine restart default】,如下所示:

然後再輸入命令【docker-machine env --shell cmd default】,如下所示:

然後把下面的內容複製下就可以了,如下所示:

這時候再輸入命令【docker version】,我們就可以顯示成功了,如下所示:

再執行命令安裝Splash即可安裝成功如下所示:

然後輸入命令【docker-machine ip default]會顯示ip地址,將下面顯示的地址複製到網頁輸出,如下所示:

這樣Splash即可安裝成功了

如上圖右側,呈現的是一個渲染示例。可以看到,上方有一個輸入框,我們點選render me按鈕開始渲染,可以看到,網頁的返回結果呈現了渲染截圖,HAR載入統計資料、網頁的原始碼。

這個過程是由一段指令碼構成,這個指令碼實際上是用Lua語言寫的指令碼,下面我們會介紹。

 

Splash Lua指令碼

Splash可以通過Lua指令碼執行一系列渲染操作。

  • 入口即返回值:

function main(splash, args)
  splash:go("http://www.baidu.com")
  splash:wait(0.5)
  local title=splash:evaljs("document.title")
  return {
    title=title
  }
end

返回了網頁的標題,這裡我們通過evaljs()方法傳入JavaScript指令碼,而document.title的執行結果就是返回網頁標題,執行完成後將其賦值給一個title變數,隨後將其返回。

我們在這裡定義的方法名稱叫做main()。這個名稱必須是固定的,Splash會預設呼叫這個方法。

該方法的返回值既可以是字典形式,也可以是字串形式,最後都會轉化為Splash HTTP Response。

  • 非同步處理

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("http://" ..url)
    if ok then         //異常檢測
      splash:wait(2)  //等待的秒數
      results[url]=splash:png()
     end
  end
  return results
end

執行結果是3個站點

 

Splash物件屬性

類似於Selenium中的WebDriver物件,我們可以呼叫它的一些屬性和方法來控制載入過程。

  • args

該屬性可以獲取載入時配置的引數,比如URL,如果為GET請求,它還可以獲取GET請求引數;如果為POST請求,它可以獲取表單提交的資料。Splash也支援使用第二個引數直接作為args,例如:

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

這個屬性是Splash的JavaScript執行開關,可以將其配置為true或false來控制是否執行Javascript程式碼,預設為true。例如,這裡禁止執行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

接著我們重新呼叫了evaljs()方法來執行JavaScript程式碼,此時執行結果就會丟擲異常:

HTTP Error 400 (Bad Request)
Type: ScriptError -> LUA_INIT_ERROR
Error happened while executing Lua script

[string "function main(splash, args)
..."]:3: function arguments expected near '='

{
    "type": "ScriptError",
    "error": 400,
    "info": {
        "source": "[string \"function main(splash, args)\r...\"]",
        "line_number": 3,
        "message": "[string \"function main(splash, args)\r...\"]:3: function arguments expected near '='",
        "type": "LUA_INIT_ERROR",
        "error": "function arguments expected near '='"
    },
    "description": "Error happened while executing Lua script"
}

 

  • resource_timeout

此屬性可以設定載入時間,單位是秒。如果設定為0或nil,代表不檢測超時,如下所示:

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

此屬性可以設定圖片是否載入,預設情況下是載入的。禁用該屬性後,可以節省網路流量並提高網頁載入速度。

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

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

 

  • plugins_enabled

此屬性可以控制瀏覽器外掛是否開啟。預設情況下是不開啟的。

  • scroll_position

通過設定此屬性,我們可以控制頁面上下或左右滾蛋。這是一個比較常用的屬性,示例如下:

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

這樣我們就可以控制頁面向下滾動400畫素值

 

Splash物件的方法

  • go()

該方法是用來請求某個連結的,而且它可以模擬GET和POST請求,同時支援傳入請求頭、表單等資料,用法如下:

ok,reason=splash:go{url,baseurl=nil,header=nil,http_method="GET",body=nil,formdata=nil}
  • url:請求的URL。
  • baseurl:可選引數,預設為空,表示資源載入相對路徑
  • headers:可選引數,預設為空,表示請求頭。
  • http_method:可選引數,預設為GET,同時支援POST。
  • body:可選引數,預設為空,發POST請求時的表單資料,使用的Content-type為application/json
  • formdata:可選引數,預設為空,POST的時候的表單資料,使用的Content-type為application/x-www-form-urlencoded。

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

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

這裡我們模擬一個POST請求,並傳入了POST的表單資料,如果成功,則返回頁面的原始碼。

 

  • wait()

此方法可以控制頁面的等待時間,使用方法如下:

ok,reason=splash:wait{time,cancel_on_redirect=false,cancel_on_error=true}
  • time:等待的秒數
  • cancel_on_redirect:可選引數,預設為false,表示如果發生了重定向就停止等待,並返回重定向結果。
  • cancel_on_error:可選引數,預設為false,表示如果發生了載入錯誤,就停止等待。

返回結果同樣是結果ok和原因reason的組合。

如下所示:

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

這可以實現訪問淘寶並等待2秒,隨後返回頁面原始碼的功能。

 

  • jsfunc()

此方法可以直接呼叫JavaScript定義的方法,但是所呼叫的方法需要用中括號包圍,這相當於實現了JavaScript方法到Lua指令碼的轉換,示例如下:

function main(splash, args)
  local get_div_count=splash:jsfunc([[
    function(){
    var body=document.body;
    var divs=body.getElementsByTagName('div');
    return divs.length;
  }
    ]])
  splash:go("https://www.baidu.com")
  return ("There are %s DIVs"):format(get_div_count())
end

我們聲明瞭一個JavaScript定義的辦法,然後在頁面載入成功後呼叫了從方法計算出了頁面中div節點的個數。

 

  • evaljs()

此方法可以執行JavaScript程式碼並返回最後一條JavaScript語句返回結果,使用方法如下:

result=splash:evaljs(js)
  • runjs()

此方法可以執行JavaScript程式碼,它與evaljs()的功能類似,但是更偏向於執行某些功能或宣告某些辦法。如下所示:

function main(splash, args)
  splash:go("https://www.baidu.com")
  splash:runjs("foo=function(){return 'bar'}")
  local result=splash:evaljs('foo()')
  return result
end

這裡我們用runjs()先聲明瞭一個JavaScript定義的方法,然後通過evaljs()來呼叫得到的結果。

 

  • autoload()

此方法可以設定每個頁面訪問時自動載入的物件,使用方法如下:

ok,reason=splash:autoload{source_or_url,source=nill,url=nil}
  • source_or_url:JavaScript程式碼或者JavaScript庫連結
  • source:JavaScript程式碼
  • url:JavaScript庫連結

此方法只負責載入JavaScript程式碼或庫,不執行任何操作。

function main(splash, args)
  splash:autoload([[
    function get_document_title(){
    return document.title
  }
    ]])
  splash:go("https:www.baidu.com")
  return splash:evaljs("get_document_title()")
end

這裡我們呼叫autoload()方法聲明瞭一個JavaScript方法,然後通過evaljs()方法來執行此JavaScript()該方法。

 

  • call_later()

此方法可以通過設定定時任務和延遲時間來實現任務延時執行,並且可以在執行前通過cancel()方法重新執行定時任務。如下所示:

function main(splash, args)
  local snapshots={}
  local timer=splash:call_later(function()
      snapshots["a"]=splash:png()
      splash:wait(1.0)
      snapshots["b"]=splash:png()
  end,0.2)
  splash:go("https://www.taobao.com")
  splash:wait(3.0)
  return snapshots
end

這裡我們設定了一個定時任務,0.2秒的時候獲取網頁截圖,然後等待1秒,1.2秒時再次獲取網頁截圖,訪問的頁面是淘寶,最後將截圖結果返回。

 

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

和http_get()方法類似,此方法用來模擬傳送POST請求,不過多了一個引數body,使用方法如下:

response=splash:http_post{url,headers=nil,follow_redirects=true,body=nil}
  • url:請求URL。
  • headers:可選引數,預設為空,請求頭
  • follow_redirects::可選引數,表示是否啟動自動重定向,預設為true。
  • body:可選引數,即表單資料,預設為空。
function main(splash, args)
  local treat=require("treat")
  local json=require("json")
  local response=splash:http_post{"http://httpbin.org/post",
    body=json.encode({name="Germey"}),
    headers={["content-type"]="application/json"}
    }
  return {
    html=treat.as_string(response.body),
    url=response.url,
    status=response.status
  }
end

這裡我們成功模擬提交了POST請求併發生了表單資料

 

  • set_content()

此方法用來設定頁面的內容,示例如下:

function main(splash)
    assert(splash:set_content("<html><body><h1>hello</h1></body></html>"))
    return splash:png()
end
  • html()

此方法用來獲取網頁的原始碼,它是非常簡單又常用的方法。示例如下:

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

此方法用來獲取PNG格式的網頁截圖,示例如下:

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

此方法用來獲取JPEG格式的網頁截圖,示例如下:

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

 

  • har()

此方法用來獲取頁面載入過程描述,示例如下:

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

此方法可以獲取當前正在訪問的URL,示例如下:

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

此方法可以獲取當前頁面的Cookies,示例如下:

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

此方法可以為當前頁面新增Cookie

cookies=splash:add_cookie{name,value,path=nil,expires=nil,httpOnly=nil,secure=nil}
  • clear_cookies()

此方法可以清楚所有的cookies

  • get_viewport_size()

此方法可以獲取當前瀏覽器頁面的大小,即寬高。

  • set_viewport_size()

此方法可以設定當前瀏覽器頁面的大小,即寬高。

  • set_viewport_full()

此方法可以設定瀏覽器全屏顯示

  • set_user_agent()

此方法可以設定瀏覽器的User-Agent

  • set_custom_headers()

此方法可以設定請求頭

  • select()

該方法可以選中符合條件的第一個節點,如果有多個節點符合條件,則只會返回一個,其引數是CSS選擇器,示例如下:

function main(splash, args)
  splash:go("https://www.baidu.com/")
  input=splash:select("#kw")
  input:send_text("Splash")
  splash:wait(3)
  return splash:png()
  
end

我們這裡首先訪問了百度,然後選中了搜尋框,隨後呼叫了send_text()方法填寫了文字,然後返回網頁截圖。

  • select_all()

此方法可以選中所有符合條件的節點,其引數是CSS選擇器。示例如下:

function main(splash, args)
  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選擇器選中了節點的正文內容,隨後遍歷了所有節點,將其中的文字獲取下來。

 

  • mouse_click()

此方法可以模擬滑鼠點選操作,傳入的引數為座標值x和y。此外,也可以直接選中某個節點,然後呼叫此方法,示例如下:

function main(splash, args)
  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

這裡我們首先選中頁面的輸入框,輸入文字,然後選中提交按鈕,呼叫了mouse_click()方法提交查詢,然後頁面等待三秒,返回截圖。

 

Splash API呼叫

  • render.html

此介面用於獲取JavaScript渲染的頁面的HTML程式碼,介面地址就是Splash的執行地址加此介面的名稱

如果用Python實現的話,如下所示:

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

這樣就可以成功輸出百度頁面渲染後的原始碼,此介面還可以指定其他引數

  • render.png

此介面可以獲取網頁截圖,其引數比render.html多了幾個,比如通過width和height來控制寬高,它返回的是PNG格式的圖片二進位制資料,示例如下:

import requests
url='http://192.168.99.100:8050/render.png?url=https://www.jd.com&wait=5&wait=1000&height=700'
response=requests.get(url)
with open('taobao.png','wb') as f:
    f.write(response.content)

這樣我們就成功獲取了京東首頁渲染完成後的頁面截圖

 

  • render.jpeg

此介面和render.png不過它返回的是JPEG格式的圖片二進位制資料

另外,此介面比render.png多了個引數quality,用來設定圖片質量。

 

  • render.har

此介面用於獲取頁面載入的HAR資料,

 

  • render.json

此介面包含了前面介面的所有功能,返回結果是JSON格式.

 

  • execute

此接口才是最為強大的介面。execute介面可以實現一些互動操作

import requests
from urllib.parse import quote
lua="""
function main(splash)
    return 'hello'
end
"""

url='http://192.168.99.100:8050/execute?lua_source='+quote(lua)
response=requests.get(url)
print(response.text)

這裡我們用Python中的三引號將Lua指令碼包括起來,然後用urllib.parse模板裡的quote()方法將指令碼進行URL轉碼,隨後構造了Splash請求URL,將其作為lua_source引數傳遞,我們通過例項看下:

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://192.168.99.100:8050/execute?lua_source='+quote(lua)
response=requests.get(url)
print(response.text)

我們所說的Lua指令碼均可以用此方法與Python進行對接。