websocket與爬蟲

原文來自websocket與爬蟲php

背景

寫爬蟲的目的應該就是爲了拿到數據,或者說模擬某種操做 若是他使用的是http(s) 協議來傳輸數據的,那麼咱們就模擬http協議來發送數據 若是它使用的是websocket協議來傳輸數據的, 那麼咱們理所固然的就模擬websocket來發送數據~html

首先,咱們須要瞭解什麼是websocketpython

websocket的介紹

WebSocket是一種在單個TCP鏈接上進行全雙工通信的協議。WebSocket通訊協議於2011年被IETF定爲標準RFC 6455,並由RFC7936補充規範。WebSocket API也被W3C定爲標準。git

WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,容許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只須要完成一次握手,二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。github

上面是維基百科的介紹. 簡單的將,websocket 和http同樣,都是一種網絡傳輸協議web

他比http協議好的地址有哪些呢?

  • 較少的控制開銷。在鏈接建立後,服務器和客戶端之間交換數據時,用於協議控制的數據包頭部相對較小。在不包含擴展的狀況下,對於服務器到客戶端的內容,此頭部大小隻有2至10字節(和數據包長度有關);對於客戶端到服務器的內容,此頭部還須要加上額外的4字節的掩碼。相對於HTTP請求每次都要攜帶完整的頭部,此項開銷顯著減小了。
  • 更強的實時性。因爲協議是全雙工的,因此服務器能夠隨時主動給客戶端下發數據。相對於HTTP請求須要等待客戶端發起請求服務端才能響應,延遲明顯更少;即便是和Comet等相似的長輪詢比較,其也能在短期內更屢次地傳遞數據。
  • 保持鏈接狀態。於HTTP不一樣的是,Websocket須要先建立鏈接,這就使得其成爲一種有狀態的協議,以後通訊時能夠省略部分狀態信息。而HTTP請求可能須要在每一個請求都攜帶狀態信息(如身份認證等)。
  • 更好的二進制支持。Websocket定義了二進制幀,相對HTTP,能夠更輕鬆地處理二進制內容。
  • 能夠支持擴展。Websocket定義了擴展,用戶能夠擴展協議、實現部分自定義的子協議。如部分瀏覽器支持壓縮等。
  • 更好的壓縮效果。相對於HTTP壓縮,Websocket在適當的擴展支持下,能夠沿用以前內容的上下文,在傳遞相似的數據時,能夠顯著地提升壓縮率

websocket的應用場景

  • 直播平臺的彈幕
  • 實時聊天
  • 等等

websocket 協議

WebSocket 是獨立的、建立在 TCP 上的協議。chrome

Websocket 經過 HTTP/1.1 協議的101狀態碼進行握手。json

爲了建立Websocket鏈接,須要經過瀏覽器發出請求,以後服務器進行迴應,這個過程一般稱爲「握手」瀏覽器

那麼websocket協議是如何握手的呢?服務器

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
複製代碼

服務器響應

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
複製代碼

和http字段不同的地方

  • Connection必須設置Upgrade,表示客戶端但願鏈接升級。
  • Upgrade字段必須設置Websocket,表示但願升級到Websocket協議。
  • Sec-WebSocket-Key是隨機的字符串,服務器端會用這些數據來構造出一個SHA-1的信息摘要。把「Sec-WebSocket-Key」加上一個特殊字符串「258EAFA5-E914-47DA-95CA-C5AB0DC85B11」,而後計算SHA-1摘要,以後進行BASE-64編碼,將結果作爲「Sec-WebSocket-Accept」頭的值,返回給客戶端。如此操做,能夠儘可能避免普通HTTP請求被誤認爲Websocket協議。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,以前草案的版本均應當棄用。
  • Origin字段是可選的,一般用來表示在瀏覽器中發起此Websocket鏈接所在的頁面,相似於Referer。可是,與Referer不一樣的是,Origin只包含了協議和主機名稱。
  • 其餘一些定義在HTTP協議中的字段,如Cookie等,也能夠在Websocket中使用。

能夠看到只是在http協議上增長了幾個硬性規定,http協議的user-agent,cookie均可以在websocket握手過程當中使用

抓包時候的注意事項:由於websocket只有一次握手,握手成功後就能夠雙方發送消息了,假如你打開網頁後沒有找到你要抓的數據,那麼你就須要從新刷新網頁,讓他從新握手一次

websocket的事件

on_open

表示剛剛鏈接的時候

onmessage

表示收到消息怎麼作

send

表示給服務器發送消息

on_close

表示關閉鏈接

那麼知道了這些對咱們有什麼好處麼? 找js的時候會很好找,這幾個關鍵詞基本上都是固定的 你能夠直接全局搜搜,而後很容易能找到發送的js代碼

模擬發送的時候也是同樣的.

實際案例

前面介紹了一堆websocket協議相關的東西,估計不少人已經暈了. 不要緊,先看實例,有問題再回到上面看

抓包可使用fiddle,chrome也是能夠的

咱們先使用chrome

本次要抓的網站的一個投票網站 你們能夠先隨便投一個票,抓抓包看看 會發現怎麼沒有找到他是如何提交數據的...

選擇ws,而後刷新下網頁,再點擊下投票,會發現有一個請求

能夠看到是在握手階段,請求頭裏面的參數和咱們上面講的是同樣的.

請求地址是ws://v5.10brandchina.com:8008/ 這邊順帶說一下,有時候這邊會看到 wss://v5.10brandchina.com:8008/ 那麼這兩個有啥區別的,簡單的講就是http與https協議的區別同樣...

看一下交互的內容(點擊Frames) 能夠看到已經有四條消息了,可是消息內容是二進制的,chrome這邊沒法預覽... 那麼咱們使用fiddle試一下

抓包與分析

打開fiddle,刷新一下網頁 不刷新的話是看不到的,而後隨便投一下票.

怎麼找到請求呢,很簡單,看狀態碼爲101的就行,而後雙擊這一行

而後這邊仍是看到四條消息,咱們點擊第一條,而後用TextView展現,能夠看到消息是這些 爲啥用TextView呢?實際上是一個一個的試過來的,假如你發現都試過了,仍是亂碼,那應該是他使用了其餘的壓縮或者加密方法,須要查看js看看他是如何加密的

這個網站的數據是沒有加密過的. 帶向上的箭頭的是咱們向服務器發送的,向下的箭頭是服務器返回的(下面的數據,前面帶黑點?,是咱們發送的)

  • {"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"}

能夠看出來 首先咱們發送{"action":"auth","val":5} 而後服務器返回一串信息給咱們, 而後咱們根據服務器返回的算出一個值,也就是 {"action":"auth","val":-0.3241458910493796} 再發送給服務器. 服務器返回{"action":"wait","msg":95420},表示驗證經過 而後咱們投票,發送了投票的一些信息給服務 服務器告訴咱們投票成功.

以上就是整個通信過程.

那若是咱們要模擬發送的話,須要知道哪些信息呢

  1. {"action":"auth","val":5}裏面的val:5,這個5是固定的麼?若是不是,是如何生成的
  2. 服務器返回的是什麼,如何解析
  3. 如何根據服務器返回的生成一個新的val
  4. 發送投票信息裏面{"action":"vote","val":"{\"itemid\":126067,\"catid\":41867,\"captcha\":\"%u7EC7%u65E7%u5F88%u9C7C\",\"auth\":5,\"rnd\":\"4186712606754595\"}"} itemid,catid,capthc,auth,rnd如何生成

找參數

仍是使用chrome,直接用ctrl + shift +f,而後輸入websocket(或者on_open,on_message,等等上面提到的事件去搜索)

運氣很好,輸入websocket直接就搜到了js,仍是沒有混淆的

首先發現 websocket 地址是根據catId變的,若是catId能被2整除則地址爲xxx,不然爲xxx 那麼catId是什麼呢,調試發現就是url中的id,咱們當前url爲http://www.10brandchina.com/vote/startin.php?id=41867則 catId爲41867

而後onmessage也看到了,大概意思是收到信息後,用json解析,若是action是auth的話,則調用sendData這個方法,若是action是vote的話,則使用vote_resule方法.

在看到onopen方法,是調用sendData,併發送('auth',authType),在這邊是否是聯想到前面,咱們第一次發送的數據?{"action":"auth","val":5},是否是感受如出一轍

close方法就不說了,反正咱們也用不上

再看看sendData這個方法,

用python實現的話是這樣

再看vote_result方法,大概做用是判斷投票結果

全部的方法咱們都找到了,那麼咱們再和以前要找的參數走一遍.

  1. {"action":"auth","val":5}裏面的val:5,這個5是固定的麼?若是不是,是如何生成的

這個5也就是onopen裏面的authType,至於authType是否是固定的,搜索一下就知道了.

  1. 服務器返回的是什麼,如何解析
  2. 如何根據服務器返回的生成一個新的val

能夠經過onmessage方法知道他返回的json數據,json解析一下就行, 裏面的val是經過執行 eval(val)獲得的 因此你也能夠直接執行這個.或者用python實現

  1. 發送投票信息裏面{"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就是驗證碼, auth和上面的authtype同樣 rnd是經過搜索js發現了.

再看看驗證碼是如何生成的呢

檢查驗證碼是否正確

咱們已經拿到全部須要的東西了,只要用程序模擬發送就好了.

模擬發送

使用的包是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

因此咱們只要用咱們上面獲得的信息就行模擬發送就能夠了

由於是投票網站,因此不提供代碼...有啥問題,請留言~

相關文章
相關標籤/搜索