1. 程式人生 > >獲取網頁中的視訊下載地址(用headless browser)

獲取網頁中的視訊下載地址(用headless browser)

介紹

前面通過兩篇文章講了怎麼去抓取HTTP的請求包,包括用代理伺服器和抓包的方法。正因為現在的視訊網站的視訊地址都不是直接在html頁面上獲取的,視訊的獲取是通過瀏覽器動態解釋js指令碼,再向視訊伺服器發去視訊請求。所以我們通過獲取瀏覽器產生的HTTP請求來獲得視訊的下載地址。

思路

直接用firefox

我們想到了可以用動態渲染js指令碼的程式,最常見的是瀏覽器。我們通過命令執行

firefox http://the.video.url

就可以用firefox開啟網址了。可是我們沒辦法在沒有顯示的情況下這樣使用,但是我們後面有方法解決這個問題。

python+selenium+Phantomjs

因為我們需要在沒有介面顯示的情況下渲染JS指令碼,我們還想到了python+selenium+Phantomjs來動態渲染頁面。Phantomjs是一個headless browser(Headless Browser就是沒有介面的瀏覽器)。
可是Phantomjs 1.5版本以後,就不再支援flash外掛了。換句話說,雖然Phantomjs能動態載入JS指令碼,但是沒辦法渲染視訊。所以不會發出獲取視訊的HTTP請求。
我通過截圖獲取渲染後的頁面,如下圖:
figure1
視訊播放視窗那裡是黑色的,抓包程式抓不到帶有flv字串的HTTP請求。
我還嘗試了1.4.1版本的Phantomjs,網上說可以通過這個命令載入Flash外掛
./bin/phantomjs --load-plugins=yes examples/snap.js


通過渲染JS指令碼,截圖得到下圖:
figure2
雖然得到的截圖跟上圖不一樣,視訊那裡多了一個載入的狀態,可是我還是抓取不到視訊HTTP請求。最後,我在python中呼叫webdriver.Phantomjs()的時候報錯.

WebDriverException: Message: Can not connect to the Service phantomjs

最後不得不放棄使用Phantomjs

slimerjs

我在網上搜索headless browser時,另外還有搜到slimerjs。

PhantomJS與SlimerJS異同:來自這篇文章

  1. PhantomJS 基於 Webkit 核心,不支援 Flash 的播放
  2. SlimerJS 基於火狐的 Gecko 核心,支援 Flash播放,並且執行過程會有頁面展示

正好我們需要支援Flash播放。可是問題又來了!SlimerJS不是一個純正的Headless browser,它需要DISPLAY!!。那麼我們有什麼辦法可以解決這個問題呢?有!

xvfb

xvfb 是通過提供一個類似 X server 守護程序 和 設定程式執行的環境變數 DISPLAY 來提供程式執行的環境
通過這個,也可以解決前面所說到的第一個Firefox遇到的問題了。感覺好像兜了個圈= =!

程式實現

最後我們決定用slimerjs來獲取頁面。因為沒辦法通過python的selenium的方法呼叫slimerjs,那麼我們只能通過python呼叫命令列程式的方法來動態渲染頁面了。slimerjs指令碼如下,我們呼叫這個指令碼的時候,傳入一個網頁地址引數。slimerjs就負責開啟這個頁面,渲染頁面的內容。
這個指令碼的檔名是getPage.js

 var page = require('webpage').create();
 var videoUrl = phantom.args[0];
 var page.open(videoUrl, function (){      
      window.setTimeout(function(){
            phantom.exit();
      },10);
});

我們python呼叫os.system("xvfb-run slimerjs getPage.js " + videoURL)來渲染視訊URL的視訊。

我們最終的python程式是slimerjs_crawl_video.py

import os
import time
open163url = 'http://open.163.com/movie/2016/1/T/D/MBCRLBLRN_MBCRM7OTD.html'
startTime = time.time()
os.system("xvfb-run slimerjs getPage.js " + open163url)
print "use time: " + str(time.time() - startTime)
➜ capture_traffic sudo python pypcap_test.py
[sudo] password for honkee:
starting capture
mov.bn.netease.com/open-movie/nos/flv/2016/01/20/SBCRM4HFN_hd.flv
mov.bn.netease.com/open-movie/nos/flv/2016/01/20/SBCRM4HFN_hd.flv?start=107570066

獲取到視訊地址!!

未解決問題

呼叫python slimerjs_crawl_video.py

➜  capture_traffic python slimerjs_craw_video.py
Vector smash protection is enabled.
use time: 94.3547639847

這個時間我還不知道怎麼控制,我試過修改getPage.jswindow.setTimeout函式的時間,可是這個用時還是90多秒,我猜測是python的os.system()的限制是阻塞一定時間(90多秒)就返回吧。接下來我再查一查相關的東西。

更新:
今天為了解決這個返回的問題,我開始查詢這個問題的原因,直接找 os.system() 這個函式的文件,它說明這個函式是阻塞的,等待命令執行完成才返回。那麼問題應該就出現在slimerjs的指令碼那裡了,偶然原因,我執行那個指令碼的時候忘了新增視訊網頁路徑引數,結果它很快就返回了。因為我 window.setTimeout() 函式設定的是10毫秒,那麼為什麼我添加了視訊網頁路徑之後就要等待90多秒呢,我打開了firefox的firebug,打開了那個網頁。發現這個函式的時間是在所有請求載入完才開始算的。下圖是firebug截圖。
figure3
兩個紅色的請求時到了90秒才表示timeout。這是計時器開始計算,加上程式載入的時間,就是90多秒了。
那麼我們自然就想到將 window.setTimeout() 函式寫在開啟頁面外面,設定時間返回。

getPage.js

 var page = require('webpage').create();
 var videoUrl = phantom.args[0];
 window.setTimeout(function(){
     phantom.exit();
},10000);
 var page.open(videoUrl, function (){      
      window.setTimeout(function(){
            phantom.exit();
      },10);
});

最後發現這個標題不太符合這篇文章。不過還是不改了,因為我本來是按著headless browser的思想去解決這個問題的