我們通常使用的抓包工具就是Fiddler和Charles這種圖形化的,Charles的優點是跨平臺,Windows和Mac都可以使用,Fiddler的優點是功能“極其”強大,不僅擁有抓包功能,還擁有中間人攻擊的功能,但是使用成本太高了,我們做爬蟲開發,使用到Fiddler的功能不過十之二三罷了。今天我們主要講的是mitmproxy這款工具,這是一款專業的中間人攻擊工具,mitmproxy 不僅可以截獲請求幫助開發者查看、分析,最最重要的是支持Python進行定製化二次開發。例如:截獲瀏覽器的請求內容,並將數據處理後存儲到數據庫,再將內容交給瀏覽器;如果出現異常時,發出郵件通知,並返回給瀏覽器一個空的頁面。mitmproxy有以下幾個特點:
- 像正常代理一樣轉發請求,保證服務器和客戶端之間的通信
- 可以攔截請求/返回,並可以修改請求/返回
- 可以加載Python腳本執行
安裝mitmproxy
<code>pip install mitmproxy/<code>
在Python環境中安裝使用pip最為簡潔。mitmproxy安裝完成以後會包含三個工具:mitmproxy、mitmdump、mitmweb。安裝完成以後直接在控制檯輸入mitmproxy --version就可以查看版本信息。
查看版本信息
注意如果是在Windows系統中安裝,需要先安裝Microsoft Visual C++ V14.0以上版本,並且mitmproxy是不能在Windows系統中進行抓包的,在執行mitmproxy --version命令的時候會得到一個錯誤提示。
<code>Error: mitmproxy's console interface is not supported on Windows. You can run mitmdump or mitmweb instead./<code>
在Windows系統中我們主要使用的是安裝完以後的另外兩個工具mitmdump和mitmweb。
安裝瀏覽器代理插件SwitchyOmega
為什麼要先安裝瀏覽器代理插件呢?因為我們在使用抓包工具的時候,必須要通過代理訪問網絡,才能抓到包,可以通過設置系統代理的方式來實現,但是直接設置瀏覽器代理會更加方便,而且使用代理插件我們可以配置多種代理模式。Chrome瀏覽器安裝插件需要科學上網,只要在度娘搜索谷歌上網助手,安裝以後重啟瀏覽器,就可以訪問谷歌商店來安裝插件了,插件我們這裡推薦SwitchyOmega。安裝完以後要進行設置。
- 打開選項
打開設置項 - 新建情景模式
- 設置代理地址和端口
然後在瀏覽器中訪問地址前,先選擇代理方式,再進行訪問
安裝證書
正常情況下,mitmproxy啟動後,只能抓取到HTTP請求的信息,我們要抓取HTTPS請求信息需要安裝證書。證書安裝有兩種方式:
第一種
- 首先打開mitmproxy進行抓包,即運行: mitmproxy或者另外兩個命令
- 訪問http://mitm.it/
如果你沒有打開mitmproxy進行抓包的話,在這一步你會得到如下錯誤
上面這種方法我一直訪問不到mitm.it這個頁面,可以採用以下方式進行安裝
第二種
- 首先打開mitmproxy進行抓包,即運行: mitmproxy或者另外兩個命令。執行了這一步以後,在操作系統對應的用戶名目錄下會產生一個.mitmproxy目錄
.cer是Mac或Linux下的證書,.p12是Windows下的證書,.pem是安卓下的證書。
通過上述兩種方式得到證書文件後,證書按照步驟在網上找,非常多,這裡就不再敖述了。
使用mitmproxy
要啟動 mitmproxy 用 mitmproxy、mitmdump、mitmweb 這三個命令中的任意一個即可,這三個命令功能一致,且都可以加載自定義腳本,唯一的區別是交互界面的不同。但是他們各有特點,mitmproxy是進行抓包調試使用的,mitmweb是mitmproxy的可視版本,mitmdump主要是加載腳本執行的,因為mitmdump抓取的信息是不主動顯示的,由我們在腳本中使用特定打印方式,輸出到界面,方便我們調試,當然也可以直接使用print打印。
在控制檯中輸入mitmdump -h,可以查看命令行幫助,我們主要使用的是-s和-p參數,-p指定監聽端口,默認端口為8080,如果和其他軟件有衝突,可以通過此參數修改;-s指定執行腳本,這個就是我們用mitmproxy的主要作用,通過加載腳本,執行請求過程的中間處理,修改請求數據或者保存返回數據。目前有兩種使用方式:
<code>from mitmproxy import httpfrom mitmproxy import ctxdef response(flow: http.HTTPFlow): """ flow為參數,後面跟http.HTTPFlow表示聲明其類型, 這樣在IDE中就可以自動提示其屬性和方法,這是Python為我們提供的一種 便攜的方式,尤其是對外提供接口時,可以告知參數類型,這種方式是可選 的,當然你也可以使用常用方式,即不知道參數類型,只寫參數名即可 """ ctx.log.info(flow.request.url) ctx.log.warn(flow.request.headers)/<code>
mitmproxy.ctx.log為mitmproxy為我們提供的日誌打印方式。
<code>from mitmproxy import httpclass Counter: def __init__(self): self.num = 0 def request(self, flow: http.HTTPFlow): self.num += 1 print("We've seen %d flows" % self.num) print(flow.request.url) print(flow.request.query)addons = [ Counter()]/<code>
官方推薦使用類的方式,上面的代碼可能讓你有點迷茫,無論是使用類方式還是函數方式def reqeust函數都是在mitmdump內部回調時會調用的,mitmdump就是使用這種事件回調的方式,為我們提供了數據流的操作方式,那首先我們要了解mitmproxy為我們提供的事件(我們只關注HTTP相關的事件)。
<code>class Events: def request(self, flow: http.HTTPFlow): """ The full HTTP request has been read. """ def response(self, flow: http.HTTPFlow): """ The full HTTP response has been read. """/<code>
request為請求發送至服務器前的回調函數,如果我們想對發送給服務器的請求進行修改,可以在這裡進行處理。response為服務器將請求數據返回給我們時的回調函數,這裡就是我們爬取到的數據,在這裡我們可以對數據進行處理,做入庫處理。
我們在爬蟲中使用mitmproxy,主要就是對Request和Response對象進行操作,下面我在源碼中把對應的屬性和方法都找出來,作為參考,就當作是字典一樣來查詢即可。源碼在GitHub上下載,路徑為:mitmproxy/net/http/request.py和mitmproxy/net/http/response.py。
Request
<code>flow.request.cookies #獲取請求的cookiesflow.request.headers # 獲取所有頭信息,包含Host、User-Agent、Content-type等字段flow.request.url # 完整的請求地址,包含域名及請求參數,但是不包含放在body裡面的請求參數flow.request.host # 域名flow.request.method # 請求方式。POST、GET等flow.request.scheme # 請求類型 ,如 http、httpsflow.request.path # 請求的路徑,url除域名之外的內容flow.request.text # 請求中body內容,可以獲取某些請求的參數,返回字典類型flow.request.replace() # 使用正則替換content中的內容flow.request.query # 返回MultiDictView類型的數據,url直接帶的鍵值參數,一般是GET請求的參數flow.request.content # bytes,結果如flow.request.textflow.request.raw_content # bytes,結果如flow.request.get_content()flow.request.urlencoded_form # MultiDictView,content-type:application/x-www-form-urlencoded 時的請求參數,不包含url直接帶的鍵值參數flow.request.multipart_form # MultiDictView,content-type:multipart/form-data 時的請求參數,不包含url直接帶的鍵值參數/<code>
Response
<code>flow.response.status_code # 狀態碼flow.response.text # 返回內容,已解碼flow.response.content # 返回內容,二進制flow.response.cookies # 返回的cookiesflow.response.headers # 返回的請求頭flow.response.replace() # 使用正則替換content中的內容/<code>
要特別注意,返回值為字典的類型的,不能直接在控制檯打印,可以使用str修飾,或者按照字典方式進行輸出。
以下為測試示例:
- 測試代碼
<code>from mitmproxy import httpclass Demo1: def request(self, flow: http.HTTPFlow): print('request url', flow.request.url) print('request name', flow.request.query.get('name')) print('request age', flow.request.query.get('age')) flow.request.query['name'] = 'yuehan'class Demo2: def response(self, flow: http.HTTPFlow): print('response name', flow.request.query.get('name'))addons = [ Demo1(), Demo2()]/<code>
- 請求url:https://httpbin.org/get?name=jieke&age=23
- 輸出結果:
示例中使用兩個類Demo1、Demo2,主要是為大家展示類方式的好處,即可以使用多個類,每個類處理進行獨立的邏輯處理,每個類當中都可以同時使用request、response函數,希望不要因為例子裡面而誤導了大家。下面再說一點進階用法,每一個處理類,都可以單獨寫一個py文件,再統一定義一個py文件,導入處理類,定義一個列表變量addons,變量中存儲所有處理類的實例,示例如下:demo1.py
<code>from mitmproxy import httpclass Demo1: def request(self, flow: http.HTTPFlow): print('request url', flow.request.url) print('request name', flow.request.query.get('name')) print('request age', flow.request.query.get('age')) flow.request.query['name'] = 'yuehan'/<code>
demo2.py
<code>from mitmproxy import httpclass Demo2: def response(self, flow: http.HTTPFlow): print('response name', flow.request.query.get('name'))/<code>
spider.py
<code>import demo1import demo2addons = [ demo1.Demo1(), demo2.Demo2()]/<code>
- 抓包命令
mitmdump -p 8888 -s spider.py
參考文章:
1.使用 mitmproxy + python 做攔截代理 https://blog.wolfogre.com/posts/usage-of-mitmproxy/
2.如何突破網站對selenium的屏蔽 https://blog.csdn.net/qq_26877377/article/details/83307208
閱讀更多 Python集結號 的文章