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

因此咱们只要用咱们上面获得的信息就行模拟发送就能够了

由于是投票网站,因此不提供代码...有啥问题,请留言~

相关文章
相关标签/搜索