Selenium爬蟲實踐(踩坑記錄)之ajax請求抓包、瀏覽器退出

曦遠發表於2021-02-03

上一篇: 使用Selenium擷取網頁上的圖片

前言

最近在搞公司內部系統,累的一批,需要從另一個內部系統匯出資料存到資料庫做分析,有大量的資料採集工作,又沒辦法去直接拿到那個系統的介面,太難了,只能爬蟲,但是cookie還經常失效,為了不每次登入失效就來找我重新注入Cookie,我寫了一個手機版的網頁,用來控制後臺的selenium自動登入,擷取token和cookie。

ajax請求抓包方案

搜尋資料的過程真的痛苦,不過還好這時間沒有白花,最終還是解決了問題……

根據找到的資料,有以下幾種方法可以在Selenium中抓取ajax請求中的資料

  • 使用本地代理:browsermob-proxy (本文采用的方法)
  • 使用selenium的執行js功能注入 ajax hook 並執行,然後本地開一個伺服器接收攔截到的ajax資料(見第三個參考資料)
  • 用第三方庫selenium-wire,這個是一個GitHub上的開源專案,可以直接擷取response_code和body,原理應該走的也是代理
  • 開啟selenium的效能抓取,在效能日誌裡面可以做改動,以攔截response_body(詳見第一個參考資料)

使用本地代理

本文使用Browsermob-Proxy這個代理伺服器,這個是用Java寫的,有一個python封裝的介面包可以方便互動……

先去下載:https://github.com/lightbody/browsermob-proxy/releases

安裝python包:

pip install browsermob-proxy

在程式碼中使用,這裡我擷取了專案的部分程式碼,隨便看看就好了,完整程式碼可以看官網文件或者參考資料~

有幾個需要注意的坑的地方,我在程式碼中標出了

# 建立代理伺服器
self.server = Server(
    # Windows就是bat,如果Linux就是另一個不帶字尾名的
    r'path\bin\browsermob-proxy.bat',
    # 這裡可以自定義埠
    options={'port': 9090}
)
# 這裡啟動伺服器,等會機會要關掉,不然下次用就埠占用衝突了
self.server.start()
# 注意這裡一定要 trustAllServers 不然等會selenium會報 error_tunnel 錯誤
self.proxy = self.server.create_proxy(params={'trustAllServers': 'true'})

# 設定selenium的代理
options = ChromeOptions()
options.add_argument('--ignore-certificate-errors')
options.add_argument(f'--proxy-server={self.proxy.proxy}')
self.driver = webdriver.Chrome(options=options)

使用代理來進行抓包,我這個專案需要在ajax請求的header裡面提取出token和cookie,擷取了關鍵部分的程式碼如下:

self.proxy.new_har('抓包名稱 自己起一個', options={'captureHeaders': True, 'captureContent': True})

# 找到需要點選的元素
elem_query = self.driver.find_element_by_css_selector(elem_css_selector)
elem_query.click()

# 點選按鈕後等待 並把資料取出來
time.sleep(5)
result = self.proxy.har

data = {}

for entry in result['log']['entries']:
    url = entry['request']['url']
    # 根據URL找到資料介面
    if 'xxx/query' in url:
        _response = entry['response']
        _content = _response['content']['text']

        for item in entry['request']['headers']:
            # 提取出header裡面的 token
            if item['name'] == 'Authorization':
                data['authorization'] = item['value']
            # 提取出header裡面的 cookie
            if item['name'] == 'Cookie':
                data['cookie'] = item['value']
        break

print(data)

以上程式碼同樣不是完整程式碼,不過已經將具體抓包的過程完整表達出來,需要的同學可以根據自己的實際需求進行編碼,只要能抓到資料,一切都好說~

瀏覽器和代理伺服器退出

這個沒啥好寫的,但是也有一個小坑,水一下吧~

從上面的程式碼裡也可以看出來,我寫了一個類來操作Selenium,程式執行完了肯定要把代理和伺服器關了,不然selenium會留著一個 chromedriver.exe 的程式在後臺佔用資源,時間一長,系統記憶體都滿了。

我在類的__del__方法中加入了關閉代理伺服器和瀏覽器的程式碼,如下:

def __del__(self):
    print('SeleniumFxxkUnicom has been deleted.')
    self.proxy.close()
    self.server.stop()
    for win in self.driver.window_handles:
        self.driver.switch_to.window(win)
        self.driver.close()
    os.system('taskkill /im chromedriver.exe /F')

注意這個迴圈的driver.close(),在__del__裡是沒辦法正常執行driver.quit()的,按理說quit才是最好的退出方法,但是他還要匯入什麼鬼亂七八糟的模組,導致我在這個__del__裡執行失敗,於是只好曲線救國,先把全部標籤頁關閉,然後用系統命令結束掉程式…… 有點硬編碼了,就這樣吧,累了

參考資料

歡迎交流

程式設計實驗室專注於網際網路熱門新技術探索與團隊敏捷開發實踐,在公眾號「程式設計實驗室」後臺回覆 linux、flutter、c#、netcore、android、kotlin、java、python 等可獲取相關技術文章和資料,同時有任何問題都可以在公眾號後臺留言~

相關文章