背景
寫爬蟲的目的應(yīng)該就是為了拿到數(shù)據(jù),或者說模擬某種操作
如果他使用的是http(s) 協(xié)議來傳輸數(shù)據(jù)的,那么我們就模擬http協(xié)議來發(fā)送數(shù)據(jù)
如果它使用的是websocket協(xié)議來傳輸數(shù)據(jù)的,
那么我們理所當(dāng)然的就模擬websocket來發(fā)送數(shù)據(jù)~
首先,我們需要了解什么是websocket
websocket的介紹
WebSocket是一種在單個TCP連接上進(jìn)行全雙工通訊的協(xié)議。WebSocket通信協(xié)議于2011年被IETF定為標(biāo)準(zhǔn)RFC 6455,并由RFC7936補(bǔ)充規(guī)范。WebSocket API也被W3C定為標(biāo)準(zhǔn)。
WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡單,允許服務(wù)端主動向客戶端推送數(shù)據(jù)。在WebSocket API中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。
上面是維基百科的介紹.
簡單的將,websocket 和http一樣,都是一種網(wǎng)絡(luò)傳輸協(xié)議
他比http協(xié)議好的地址有哪些呢?
- 較少的控制開銷。在連接創(chuàng)建后,服務(wù)器和客戶端之間交換數(shù)據(jù)時,用于協(xié)議控制的數(shù)據(jù)包頭部相對較小。在不包含擴(kuò)展的情況下,對于服務(wù)器到客戶端的內(nèi)容,此頭部大小只有2至10字節(jié)(和數(shù)據(jù)包長度有關(guān));對于客戶端到服務(wù)器的內(nèi)容,此頭部還需要加上額外的4字節(jié)的掩碼。相對于HTTP請求每次都要攜帶完整的頭部,此項(xiàng)開銷顯著減少了。
- 更強(qiáng)的實(shí)時性。由于協(xié)議是全雙工的,所以服務(wù)器可以隨時主動給客戶端下發(fā)數(shù)據(jù)。相對于HTTP請求需要等待客戶端發(fā)起請求服務(wù)端才能響應(yīng),延遲明顯更少;即使是和Comet等類似的長輪詢比較,其也能在短時間內(nèi)更多次地傳遞數(shù)據(jù)。
- 保持連接狀態(tài)。于HTTP不同的是,Websocket需要先創(chuàng)建連接,這就使得其成為一種有狀態(tài)的協(xié)議,之后通信時可以省略部分狀態(tài)信息。而HTTP請求可能需要在每個請求都攜帶狀態(tài)信息(如身份認(rèn)證等)。
- 更好的二進(jìn)制支持。Websocket定義了二進(jìn)制幀,相對HTTP,可以更輕松地處理二進(jìn)制內(nèi)容。
- 可以支持?jǐn)U展。Websocket定義了擴(kuò)展,用戶可以擴(kuò)展協(xié)議、實(shí)現(xiàn)部分自定義的子協(xié)議。如部分瀏覽器支持壓縮等。
- 更好的壓縮效果。相對于HTTP壓縮,Websocket在適當(dāng)?shù)臄U(kuò)展支持下,可以沿用之前內(nèi)容的上下文,在傳遞類似的數(shù)據(jù)時,可以顯著地提高壓縮率
websocket的應(yīng)用場景
- 直播平臺的彈幕
- 實(shí)時聊天
- 等等
websocket 協(xié)議
WebSocket 是獨(dú)立的、創(chuàng)建在 TCP 上的協(xié)議。
Websocket 通過 HTTP/1.1 協(xié)議的101狀態(tài)碼進(jìn)行握手。
為了創(chuàng)建Websocket連接,需要通過瀏覽器發(fā)出請求,之后服務(wù)器進(jìn)行回應(yīng),這個過程通常稱為“握手”
那么websocket協(xié)議是如何握手的呢?
websocket握手
下面是websocket一次握手的過程
客戶端請求
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
服務(wù)器響應(yīng)
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
和http字段不一樣的地方
- Connection必須設(shè)置Upgrade,表示客戶端希望連接升級。
- Upgrade字段必須設(shè)置Websocket,表示希望升級到Websocket協(xié)議。
- Sec-WebSocket-Key是隨機(jī)的字符串,服務(wù)器端會用這些數(shù)據(jù)來構(gòu)造出一個SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一個特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后計(jì)算SHA-1摘要,之后進(jìn)行BASE-64編碼,將結(jié)果做為“Sec-WebSocket-Accept”頭的值,返回給客戶端。如此操作,可以盡量避免普通HTTP請求被誤認(rèn)為Websocket協(xié)議。
- Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均應(yīng)當(dāng)棄用。
- Origin字段是可選的,通常用來表示在瀏覽器中發(fā)起此Websocket連接所在的頁面,類似于Referer。但是,與Referer不同的是,Origin只包含了協(xié)議和主機(jī)名稱。
- 其他一些定義在HTTP協(xié)議中的字段,如Cookie等,也可以在Websocket中使用。
可以看到只是在http協(xié)議上增加了幾個硬性規(guī)定,http協(xié)議的user-agent,cookie都可以在websocket握手過程中使用
抓包時候的注意事項(xiàng):因?yàn)閣ebsocket只有一次握手,握手成功后就可以雙方發(fā)送消息了,假如你打開網(wǎng)頁后沒有找到你要抓的數(shù)據(jù),那么你就需要重新刷新網(wǎng)頁,讓他重新握手一次
websocket的事件
on_open
表示剛剛連接的時候
onmessage
表示收到消息怎么做
send
表示給服務(wù)器發(fā)送消息
on_close
表示關(guān)閉連接
那么知道了這些對我們有什么好處么?
找js的時候會很好找,這幾個關(guān)鍵詞基本上都是固定的
你可以直接全局搜搜,然后很容易能找到發(fā)送的js代碼
模擬發(fā)送的時候也是一樣的.
實(shí)際案例
前面介紹了一堆websocket協(xié)議相關(guān)的東西,估計(jì)很多人已經(jīng)暈了.
沒關(guān)系,先看實(shí)例,有問題再回到上面看
抓包可以使用fiddle,chrome也是可以的
我們先使用chrome
本次要抓的網(wǎng)站的一個投票網(wǎng)站
大家可以先隨便投一個票,抓抓包看看
會發(fā)現(xiàn)怎么沒有找到他是如何提交數(shù)據(jù)的...
選擇ws,然后刷新下網(wǎng)頁,再點(diǎn)擊下投票,會發(fā)現(xiàn)有一個請求

可以看到是在握手階段,請求頭里面的參數(shù)和我們上面講的是一樣的.
請求地址是ws://v5.10brandchina.com:8008/
這邊順帶說一下,有時候這邊會看到 wss://v5.10brandchina.com:8008/
那么這兩個有啥區(qū)別的,簡單的講就是http與https協(xié)議的區(qū)別一樣...

看一下交互的內(nèi)容(點(diǎn)擊Frames)
可以看到已經(jīng)有四條消息了,但是消息內(nèi)容是二進(jìn)制的,chrome這邊無法預(yù)覽...
那么我們使用fiddle試一下

抓包與分析
打開fiddle,刷新一下網(wǎng)頁
不刷新的話是看不到的,然后隨便投一下票.

怎么找到請求呢,很簡單,看狀態(tài)碼為101的就行,然后雙擊這一行
然后這邊還是看到四條消息,我們點(diǎn)擊第一條,然后用TextView展示,可以看到消息是這些
為啥用TextView呢?其實(shí)是一個一個的試過來的,假如你發(fā)現(xiàn)都試過了,還是亂碼,那應(yīng)該是他使用了其他的壓縮或者加密方法,需要查看js看看他是如何加密的

這個網(wǎng)站的數(shù)據(jù)是沒有加密過的.
帶向上的箭頭的是我們向服務(wù)器發(fā)送的,向下的箭頭是服務(wù)器返回的(下面的數(shù)據(jù),前面帶黑點(diǎn)?,是我們發(fā)送的)
{"action":"auth","val":5}
{"action":"auth","msg":"eval(\"\\115\\141\\164\\150\\56\\163\\151\\156\\50\\61\\65\\61\\67\\67\\66\\62\\63\\61\\63\\51\")"}
{"action":"auth","val":-0.3241458910493796}
{"action":"wait","msg":95420}
{"action":"vote","val":"{\"itemid\":126067,\"catid\":41867,\"captcha\":\"%u7EC7%u65E7%u5F88%u9C7C\",\"auth\":5,\"rnd\":\"4186712606754595\"}"}
{"action":"vote","msg":"ok,231812,2018-02-04 22:32:55"}
可以看出來
首先我們發(fā)送{"action":"auth","val":5}
然后服務(wù)器返回一串信息給我們,
然后我們根據(jù)服務(wù)器返回的算出一個值,也就是
{"action":"auth","val":-0.3241458910493796}
再發(fā)送給服務(wù)器.
服務(wù)器返回{"action":"wait","msg":95420},表示驗(yàn)證通過
然后我們投票,發(fā)送了投票的一些信息給服務(wù)
服務(wù)器告訴我們投票成功.
以上就是整個通訊過程.
那如果我們要模擬發(fā)送的話,需要知道哪些信息呢
-
{"action":"auth","val":5}里面的val:5,這個5是固定的么?如果不是,是如何生成的 - 服務(wù)器返回的是什么,如何解析
- 如何根據(jù)服務(wù)器返回的生成一個新的val
- 發(fā)送投票信息里面
{"action":"vote","val":"{\"itemid\":126067,\"catid\":41867,\"captcha\":\"%u7EC7%u65E7%u5F88%u9C7C\",\"auth\":5,\"rnd\":\"4186712606754595\"}"}
itemid,catid,capthc,auth,rnd如何生成
找參數(shù)
還是使用chrome,直接用ctrl + shift +f,然后輸入websocket(或者on_open,on_message,等等上面提到的事件去搜索)
運(yùn)氣很好,輸入websocket直接就搜到了js,還是沒有混淆的

首先發(fā)現(xiàn) websocket 地址是根據(jù)catId變的,如果catId能被2整除則地址為xxx,否則為xxx
那么catId是什么呢,調(diào)試發(fā)現(xiàn)就是url中的id,我們當(dāng)前url為http://www.10brandchina.com/vote/startin.php?id=41867則 catId為41867
然后onmessage也看到了,大概意思是收到信息后,用json解析,如果action是auth的話,則調(diào)用sendData這個方法,如果action是vote的話,則使用vote_resule方法.

在看到onopen方法,是調(diào)用sendData,并發(fā)送('auth',authType),在這邊是不是聯(lián)想到前面,我們第一次發(fā)送的數(shù)據(jù)?{"action":"auth","val":5},是不是感覺一模一樣
close方法就不說了,反正我們也用不上

再看看sendData這個方法,

用python實(shí)現(xiàn)的話是這樣

再看vote_result方法,大概作用是判斷投票結(jié)果

所有的方法我們都找到了,那么我們再和之前要找的參數(shù)走一遍.
-
{"action":"auth","val":5}里面的val:5,這個5是固定的么?如果不是,是如何生成的
這個5也就是onopen里面的authType,至于authType是不是固定的,搜索一下就知道了.
- 服務(wù)器返回的是什么,如何解析
- 如何根據(jù)服務(wù)器返回的生成一個新的val
可以通過onmessage方法知道他返回的json數(shù)據(jù),json解析一下就行,
里面的val是通過執(zhí)行 eval(val)得到的
所以你也可以直接執(zhí)行這個.或者用python實(shí)現(xiàn)

- 發(fā)送投票信息里面
{"action":"vote","val":"{\"itemid\":126067,\"catid\":41867,\"captcha\":\"%u7EC7%u65E7%u5F88%u9C7C\",\"auth\":5,\"rnd\":\"4186712606754595\"}"}
itemid,catid,capthc,auth,rnd如何生成
itemid 就是你投票的公司的id,catid之前講過,captcha就是驗(yàn)證碼,
auth和上面的authtype一樣
rnd是通過搜索js發(fā)現(xiàn)了.

再看看驗(yàn)證碼是如何生成的呢

檢查驗(yàn)證碼是否正確

我們已經(jīng)拿到所有需要的東西了,只要用程序模擬發(fā)送就行了.
模擬發(fā)送
使用的包是websocket
官方demo
import websocket
try:
import thread
except ImportError:
import _thread as thread
import time
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
def run(*args):
for i in range(3):
time.sleep(1)
ws.send("Hello %d" % i)
time.sleep(1)
ws.close()
print("thread terminating...")
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://echo.websocket.org/",
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
可以看到使用還是很簡單的,也是onopen,onmessage,send
所以我們只要用我們上面得到的信息就行模擬發(fā)送就可以了
因?yàn)槭峭镀本W(wǎng)站,所以不提供代碼...有啥問題,請留言~