輕松截獲 Selenium 中的 Ajax 數據


這篇文章同樣的還是轉載崔大的,因為都是一個系列的,所以我就轉載出來了,我覺得很實用。原文鏈接:點我

以下內容為原文。

 

之前我們介紹了 ajax-hook 來實現爬蟲的過程中截獲 Ajax 請求,可以看這篇文章如何用 Hook 實時處理和保存 Ajax 數據,在這里再另外介紹一個工具 BrowserMob Proxy,利用它我們同樣可以實現 Selenium 爬蟲過程中 Ajax 請求的獲取。

下面我們來簡單介紹一下。

 

BrowserMob Proxy

BrowserMob Proxy,簡稱 BMP,它是一個 HTTP 代理服務,利用它我們可以截獲 HTTP 請求和響應內容,另外還可以把 Performance data 輸出成一個 HAR 文件。

其 GitHub 鏈接為:https://github.com/lightbody/browsermob-proxy/

大家可以點擊進去看看詳情介紹。

實際上其原理就是開了一個代理服務器,然后抓包,同時對接了 Java、Python API,以方便我們可以直接通過代碼來獲取到內容。

案例

官方的一些介紹比較復雜,而且大多數都是 Java 的對接,在這里我們使用 Python 來實驗一下。

這里我們就直接通過一個案例來測試下吧,廢話不多說。

還是拿我自己的一個測試網站為案例,鏈接為:https://dynamic2.scrape.center/

頁面如圖所示:

 

 

 

 

其數據都是通過 Ajax 加載的,同時帶着一些加密參數:

 

 

 

這個網站通過 Selenium 爬的話一點問題也沒有,但是由於數據本身就是從 Ajax 加載的,所以如果能直接截獲 Ajax 請求的話,連頁面解析都省了。

所以這里我們要利用 BrowserMob Proxy 來截獲一下試試。

代碼實現

要用 Python 實現,我們需要先安裝一個 BrowserMob Proxy 的包,命令如下:

 

pip3 install browsermob-proxy

  

另外我們還需要下載 browsermob-proxy 的二進制文件,以便於啟動 BrowserMob Proxy。

下載的地址見:https://github.com/lightbody/browsermob-proxy/releases

直接下載 build 過的版本即可:

 

 

 

 

比如這里我就下載 browsermob-proxy-2.1.4.zip 文件,直接放到我的項目目錄下。

好,接着呢,我們就可以實現如下代碼:

from browsermobproxy import Server
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# 啟動代理
server = Server('./browsermob-proxy-2.1.4/bin/browsermob-proxy')
server.start()
proxy = server.create_proxy()
print('proxy', proxy.proxy)

# 啟動瀏覽器
chrome_options = Options()
chrome_options.add_argument('--ignore-certificate-errors')
chrome_options.add_argument('--proxy-server={0}'.format(proxy.proxy))
driver = webdriver.Chrome(options=chrome_options)

# 監聽結果
base_url = 'https://dynamic2.scrape.center/'
proxy.new_har(options={
    'captureContent': True,
    'captureHeaders': True
})
driver.get(base_url)
time.sleep(3)

# 讀取結果
result = proxy.har
for entry in result['log']['entries']:
    print(entry['request']['url'])
    print(entry['response']['content'])

  

在這里呢,一共分了四步:

•第一步便是啟動 BrowserMob Proxy,它會在本地啟動一個代理服務,這里注意 Server 的第一個參數需要指定 BrowserMob Proxy 的可執行文件路徑,這里我就指定了下載下來的 BrowserMob Proxy 的 bin 目錄的 browsermob-proxy 的路徑。

•第二步便是啟動 Selenium 了,它可以設置 Proxy Server 為 BrowserMob Proxy 的地址。

•第三步便是訪問頁面同時監聽結果,這里我們需要調用 new_har 方法,同時指定捕獲 Resopnse Body 和 Headers 信息,緊接着調用 Selenium 的 get 方法訪問一個頁面,這時候瀏覽器便會加載這個頁面,同時所有的請求和響應信息都會被記錄到 HAR 中。

•第四步便是讀取 HAR 到內容了,我們調用 log 到 entries 字段,里面便包含了請求和響應的具體結果,這樣所有的請求和響應信息我們便能獲取到了,Ajax 的內容也不在話下。

運行結果類似如下:

 

 

 

這里可以看到所有的數據都能獲取到了,包括 Ajax 結果、JavaScript、CSS 文件內容等等。

這里 har 的內容其實是一個 JSON 對象,里面記錄了在訪問頁面的過程中發生的所有請求和響應內容,一般內容都會記錄在 logs 的 entries 字段里面,還有其他的信息如有需要也可以讀取。

所以,這樣我們就能從 Selenium 中獲取 Ajax 請求內容了。

優化

不過像上面這種代碼還是不方便啊,不好復用,不好擴展,我們來稍微改寫下,代碼如下:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from browsermobproxy import Server
import time
import json

class BaseFramework(object):

    def __init__(self):
        self.server = Server('./browsermob-proxy-2.1.4/bin/browsermob-proxy')
        self.server.start()
        self.proxy = self.server.create_proxy()
        chrome_options = Options()
        chrome_options.add_argument('--ignore-certificate-errors')
        chrome_options.add_argument('--proxy-server={0}'.format(self.proxy.proxy))
        self.browser = webdriver.Chrome(options=chrome_options)

    def process_request(self, request, response):
        pass

    def process_response(self, response, request):
        pass

    def run(self, func, *args):
        self.proxy.new_har(options={
            'captureContent': True,
            'captureHeaders': True
        })
        func(*args)
        result = self.proxy.har
        for entry in result['log']['entries']:
            request = entry['request']
            response = entry['response']
            self.process_request(request, response)
            self.process_response(response, request)

    def __del__(self):
        self.proxy.close()
        self.browser.close()


class MovieFramework(BaseFramework):

    def process_request(self, request, response):
        print(request)

    def process_response(self, response, request):
        if '/api/movie/' in request['url']:
            print(response['content'])
            text = response['content']['text']
            results = json.loads(text)['results']
            for item in results:
                name = item.get('name')
                with open(f'{name}.json', 'w', encoding='utf-8') as f:
                    json.dump(item, f, ensure_ascii=False, indent=2)

    def load(self, url):
        self.browser.get(url)
        time.sleep(3)

if __name__ == '__main__':
    f = MovieFramework()
    for page in range(1, 5):
        url = f'https://dynamic2.scrape.center/page/{page}'
        f.run(f.load, url)

  

這里框架寫的很基礎,還有很多需要完善的地方,就只借着這雛形說說大體思路:

•這里我先定義了一個 BaseFramework,就是基礎框架,然后里面定義了幾個關鍵方法,__init__ 方法不多說了,就是把一些初始化的工作放進去。然后定義了 run 方法,把 HAR 的聲明、訪問、讀取的操作封裝了一下。然后定義了 process_response 和 process_request 的回調,這里就沒實現任何操作,可以在子類實現。•如果我們要寫一個爬蟲的話,可以新建一個子類來繼承剛才定義的 BaseFramework,然后可以自己實現 process_request 和 process_response 來處理請求和響應的結果,比如這里我就實現了一個 MovieFramework,然后實現了 process_response 處理響應信息,里面判斷了 Ajax 請求的 URL,然后進行了提取和保存處理。里面 load 方法就是自行定義的,里面正常定義邏輯即可。•最后運行的時候使用 run 方法運行自定義的 load 方法即可,傳入 load 方法的參數,即可完成頁面的加載。同時加載的過程中 process_response 方法就會被回調,對結果進行處理。這里我們就提取了 Ajax 數據,然后保存下來了。

最終運行下,我們就可以看到一條條的電影數據就被保存下來了,如圖所示:

 

 

 


是不是方便多了?有了它我們連頁面解析的那一步都直接省略了,直接拿到了原始 Ajax 數據,舒服。

當然上面的框架還有很多很多需要優化的地方,大家可以參考思路自己實現。

 

 

 

 

 

 


免責聲明!

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



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