1. 程式人生 > >小白學 Python 爬蟲(39): JavaScript 渲染服務 scrapy-splash 入門

小白學 Python 爬蟲(39): JavaScript 渲染服務 scrapy-splash 入門

人生苦短,我用 Python

前文傳送門:

小白學 Python 爬蟲(1):開篇

小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝

小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門

小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門

小白學 Python 爬蟲(5):前置準備(四)資料庫基礎

小白學 Python 爬蟲(6):前置準備(五)爬蟲框架的安裝

小白學 Python 爬蟲(7):HTTP 基礎

小白學 Python 爬蟲(8):網頁基礎

小白學 Python 爬蟲(9):爬蟲基礎

小白學 Python 爬蟲(10):Session 和 Cookies

小白學 Python 爬蟲(11):urllib 基礎使用(一)

小白學 Python 爬蟲(12):urllib 基礎使用(二)

小白學 Python 爬蟲(13):urllib 基礎使用(三)

小白學 Python 爬蟲(14):urllib 基礎使用(四)

小白學 Python 爬蟲(15):urllib 基礎使用(五)

小白學 Python 爬蟲(16):urllib 實戰之爬取妹子圖

小白學 Python 爬蟲(17):Requests 基礎使用

小白學 Python 爬蟲(18):Requests 進階操作

小白學 Python 爬蟲(19):Xpath 基操

小白學 Python 爬蟲(20):Xpath 進階

小白學 Python 爬蟲(21):解析庫 Beautiful Soup(上)

小白學 Python 爬蟲(22):解析庫 Beautiful Soup(下)

小白學 Python 爬蟲(23):解析庫 pyquery 入門

小白學 Python 爬蟲(24):2019 豆瓣電影排行

小白學 Python 爬蟲(25):爬取股票資訊

小白學 Python 爬蟲(26):為啥買不起上海二手房你都買不起

小白學 Python 爬蟲(27):自動化測試框架 Selenium 從入門到放棄(上)

小白學 Python 爬蟲(28):自動化測試框架 Selenium 從入門到放棄(下)

小白學 Python 爬蟲(29):Selenium 獲取某大型電商網站商品資訊

小白學 Python 爬蟲(30):代理基礎

小白學 Python 爬蟲(31):自己構建一個簡單的代理池

小白學 Python 爬蟲(32):非同步請求庫 AIOHTTP 基礎入門

小白學 Python 爬蟲(33):爬蟲框架 Scrapy 入門基礎(一)

小白學 Python 爬蟲(34):爬蟲框架 Scrapy 入門基礎(二)

小白學 Python 爬蟲(35):爬蟲框架 Scrapy 入門基礎(三) Selector 選擇器

小白學 Python 爬蟲(36):爬蟲框架 Scrapy 入門基礎(四) Downloader Middleware

小白學 Python 爬蟲(37):爬蟲框架 Scrapy 入門基礎(五) Spider Middleware

小白學 Python 爬蟲(38):爬蟲框架 Scrapy 入門基礎(六) Item Pipeline

引言

Splash 是一種 JavaScript 渲染服務,是一個帶有 HTTP API 的輕量級瀏覽器,同時它對接了 Python3 中的 Twisted 和 QT 庫。

通過它,我們同樣可以實現動態渲染頁面的抓取。

Github:https://github.com/scrapy-plugins/scrapy-splash

Splash 官方文件:http://splash.readthedocs.io

功能說明:

  • 並行處理多個網頁;
  • 獲取 HTML 結果和/或獲取螢幕截圖;
  • 關閉圖片或使用 Adblock Plus 規則來加快渲染速度;
  • 在頁面上下文中執行自定義 JavaScript;
  • 編寫 Lua 瀏覽指令碼 ;
  • 在 Splash-Jupyter Notebook 中開發 Splash Lua 指令碼。
  • 以 HAR 格式獲取詳細的渲染資訊。

安裝

安裝 Splash 主要有兩個部分,一個是 Splash 服務的安裝,具體是通過Docker,安裝之後,會啟動一個 Splash 服務。另外一個是 Scrapy-Splash 的 Python 庫的安裝,安裝之後即可在 Scrapy 中使用 Splash 服務。

在 Docker 中安裝 Splash 服務,命令如下:

docker run -p 8050:8050 scrapinghub/splash

理論上看到如下內容,就證明安裝成功了。

2020-01-10 13:09:41+0000 [-] Log opened.
2020-01-10 13:09:41.824978 [-] Xvfb is started: ['Xvfb', ':1196586140', '-screen', '0', '1024x768x24', '-nolisten', 'tcp']
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-splash'
2020-01-10 13:09:42.153188 [-] Splash version: 3.4
2020-01-10 13:09:42.376664 [-] Qt 5.13.1, PyQt 5.13.1, WebKit 602.1, Chromium 73.0.3683.105, sip 4.19.19, Twisted 19.7.0, Lua 5.2
2020-01-10 13:09:42.376820 [-] Python 3.6.8 (default, Oct  7 2019, 12:59:55) [GCC 8.3.0]
2020-01-10 13:09:42.376898 [-] Open files limit: 1048576
2020-01-10 13:09:42.376965 [-] Can't bump open files limit
2020-01-10 13:09:42.394903 [-] proxy profiles support is enabled, proxy profiles path: /etc/splash/proxy-profiles
2020-01-10 13:09:42.395050 [-] memory cache: enabled, private mode: enabled, js cross-domain access: disabled
2020-01-10 13:09:42.594670 [-] verbosity=1, slots=20, argument_cache_max_entries=500, max-timeout=90.0
2020-01-10 13:09:42.594909 [-] Web UI: enabled, Lua: enabled (sandbox: enabled), Webkit: enabled, Chromium: enabled
2020-01-10 13:09:42.595245 [-] Site starting on 8050
2020-01-10 13:09:42.595341 [-] Starting factory <twisted.web.server.Site object at 0x7f26e5414fd0>
2020-01-10 13:09:42.595541 [-] Server listening on http://0.0.0.0:8050

這時我們開啟瀏覽器直接訪問 http://localhost:8050 ,就能看到 Splash 的主頁:

接下來安裝 Scrapy-Splash 的 Python 庫,這個就比較簡單了,一個命令搞定:

pip install scrapy-splash

試用

開啟 Splash 的主頁,可以看到輸入框中預設訪問的是 http://google.com ,我們這裡換成度孃的首頁看下:

可以看到,網頁的返回結果呈現了渲染截圖、 HAR 載入統計資料、網頁的原始碼。

通過 HAR 的結果可以看到, Splash 執行了整個網頁的渲染過程,包括 CSS 、 JavaScript 的載入等過程,呈現的頁面和我們在瀏覽器中得到的結果完全一致。

點選上方的 Script 按鈕,可以看到一段指令碼,如下:

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 的整個渲染都是由這個 Lua 指令碼進行控制的。

雖然我們並不清楚 Lua 指令碼的語法,但是看了這個程式碼,也應該能大致猜測出來首先是使用 go() 訪問了 url ,然後使用 wait() 等待了 0.5 秒。最後返回了頁面的 html 原始碼,png 的截圖和 har 的一些資料。

Splash Lua API

我們來簡單的瞭解一下 Splash Lua 的一些內建的 API ,更多的內容可以訪問文件獲得,小編這裡主要介紹一下有關頁面操作的 API 。

Splash Lua API 文件地址:https://splash.readthedocs.io/en/stable/scripting-overview.html

導航

  • splash:go - 向瀏覽器載入URL;
  • splash:set_content - 將指定的內容(通常是HTML)載入到瀏覽器;
  • splash:lock_navigation and splash:unlock_navigation - 鎖定/解鎖導航;
  • splash:on_navigation_locked 允許檢查鎖定導航後丟棄的請求;
  • splash:set_user_agent 允許更改用於請求的User-Agent標頭;
  • splash:set_custom_headers 允許設定預設的HTTP標頭Splash使用。
  • splash:on_request 允許過濾或替換對相關資源的請求;它還允許根據請求設定HTTP或SOCKS5代理伺服器;
  • splash:on_response_headers 允許根據請求的標頭(例如,基於Content-Type)過濾掉請求;
  • splash:init_cookies, splash:add_cookie, splash:get_cookies, splash:clear_cookies and splash:delete_cookies 管理cookie。

延遲

  • splash:wait 允許等待指定的時間;
  • splash:call_later 計劃將來的任務;
  • splash:wait_for_resume 允許等待直到某個JS事件發生;
  • splash:with_timeout 允許限制在程式碼塊中花費的時間。

從頁面中提取資訊

  • splash:html 在由瀏覽器呈現後返回頁面HTML內容;
  • splash:url 返回瀏覽器中載入的當前URL;
  • splash:evaljs and splash:jsfunc 允許使用JavaScript從頁面提取資料;
  • splash:select and splash:select_all 允許在頁面中執行CSS選擇器;它們返回Element物件,該物件具有許多對抓取和進一步處理有用的方法
  • element:text 返回DOM元素的文字內容;
  • element:bounds 返回元素的邊界框;
  • element:styles 返回元素的計算樣式;
  • element:form_values 返回<form>元素的值。

截圖

  • splash:png, splash:jpeg - 拍攝PNG或JPEG螢幕截圖;
  • splash:set_viewport_full - 更改視口大小(在 splash:png 或 splash:jpeg 之前呼叫 )以獲取整個頁面的螢幕截圖;
  • splash:set_viewport_size - 更改視口的大小;
  • element:png and element:jpeg - 擷取單個DOM元素的螢幕截圖。

與頁面互動

  • splash:runjs, splash:evaljs and splash:jsfunc 允許在頁面上下文中執行任意JavaScript;
  • splash:autoload 允許在每個頁面渲染開始時預載入JavaScript庫或執行一些JavaScript程式碼;
  • splash:mouse_click, splash:mouse_hover, splash:mouse_press, splash:mouse_release 允許將滑鼠事件傳送到頁面上的特定座標;
  • element:mouse_click and element:mouse_hover 允許將滑鼠事件傳送到特定的DOM元素;
  • splash:send_keys and splash:send_text 允許將鍵盤事件傳送到頁面;
  • element:send_keys and element:send_text 允許將鍵盤事件傳送到特定的DOM元素;
  • 可以<form>使用element:form_values獲取初始值,在Lua程式碼中對其進行更改,使用element:fill用更新後的值填充表單,並使用element:submit提交它 ;
  • splash.scroll_position 允許滾動頁面;

HTTP請求

  • splash:http_get - 傳送HTTP GET請求並獲得響應,而無需將頁面載入到瀏覽器;
  • splash:http_post - 傳送HTTP POST請求並獲得響應,而無需將頁面載入到瀏覽器;

檢查網路流量

  • splash:har 以 HAR 格式返回所有請求和響應
  • splash:history 返回有關重定向和載入到瀏覽器主視窗的頁面的資訊;
  • splash:on_request 允許捕獲網頁和指令碼發出的請求;
  • splash:on_response_headers 允許在標頭到達時檢查(或丟棄)響應;
  • splash:on_response 允許檢查收到的原始響應(包括相關資源的內容);
  • splash.response_body_enabled 在 splash:har 和 splash:on_response 中啟用完整的響應主體 ;

示例

上面講了這麼這麼多 API ,我們寫一個簡單的小例子吧:

function main(splash, args)
  splash:set_viewport_size(400, 700)
  assert(splash:go(args.url))
  assert(splash:wait(0.5))
  return {
    url = splash:url(),
    jpeg = splash:jpeg(),
    har = splash:har(),
    cookies = splash:get_cookies()
  }
end

這裡小編設定了當前瀏覽器頁面的大小,返回了當前訪問的 url ,並且將返回的圖片格式改成了 jpeg ,同時返回了當前的 cookies 。

結果太長了,小編這裡就不截圖了,各位同學可以自己動手嘗試下,屬實很簡單,並不難。

Splash HTTP API

上面我們介紹瞭如何在 Splash 主頁上通過 Lua 指令碼進行一些操作,但這並不是我們想要的,我們想要通過我們自己 Python 程式來結合 Splash 對頁面進行抓取。

Splash 給我們提供了一些 HTTP API 介面,我們只需要請求這些介面並傳遞相應的引數即可,下面我們簡單的介紹一下這些介面的使用。

更多內容可以查閱文件:https://splash.readthedocs.io/en/stable/api.html

render.html

此介面用於獲取JavaScript渲染的頁面的HTML程式碼,介面地址就是Splash的執行地址加此介面名稱,例如 http://localhost:8050/render.html 。

比如我們使用某東做測試:

import requests

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

render.html 其實還支援很多引數,具體內容可以查閱文件獲得。

render.png

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

import requests

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

這裡我們可以看到當前目錄下多了一張名為 jd.png 的圖片,如下:

render.har

此介面用於獲取頁面載入的HAR資料,示例如下:

import requests

url = 'http://localhost:8050/render.har?url=https://www.jd.com'
response = requests.get(url)
print(response.text)

結果太長了,小編就不貼了,結果是一個JSON格式的資料,其中包含頁面載入過程中的 HAR 資料。

render.json

此介面包含了前面介面的所有功能,返回結果是JSON格式,示例如下:

url = 'http://localhost:8050/render.json?url=https://httpbin.org'
response = requests.get(url)
print(response.text)

結果如下:

{"url": "https://httpbin.org/get", "requestedUrl": "https://httpbin.org/get", "geometry": [0, 0, 1024, 768], "title": ""}

我們可以通過傳入不同引數控制其返回結果。比如,傳入 html=1 ,返回結果即會增加原始碼資料;傳入 png=1 ,返回結果即會增加頁面PNG截圖資料;傳入 har=1 ,則會獲得頁面 HAR 資料。示例程式碼如下:

url = 'http://localhost:8050/render.json?url=https://httpbin.org/get&html=1&har=1'
response = requests.get(url)
print(response.text)

execute

此接口才最為強大的介面,我們前面用的那些 Lua 指令碼,就是通過這個介面來與 Splash 進行對接的,我們將剛才上面的示例稍微改動下,程式碼如下:

import requests
from urllib.parse import quote

lua = '''
function main(splash, args)
  splash:go("https://www.geekdigging.com/")
    return {
      url = splash:url(),
      jpeg = splash:jpeg(),
      har = splash:har(),
      cookies = splash:get_cookies()
    }
end
'''

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

結果同樣有點長,小編就不貼了。

本篇內容就到這裡了,感謝觀看。

示例程式碼

本系列的所有程式碼小編都會放在程式碼管理倉庫 Github 和 Gitee 上,方便大家取用。

示例程式碼-Github

示例程式碼-Gitee

參考

https://splash.readthedocs.io/en/stable/scripting-overview.html

https://splash.readthedocs.io/en/stable/api.html

https://github.com/scrapy-plugins/scrapy-splash

https://splash.readthedocs.io/en/stable/

https://cuiqingcai.com/5638.h