写爬虫的目的应该就是为了拿到数据,或者说模拟某种操作 如果他使用的是http(s) 协议来传输数据的,那么我们就模拟http协议来发送数据 如果它使用的是websocket协议来传输数据的, 那么我们理所当然的就模拟websocket来发送数据~
首先,我们需要了解什么是websocket
WebSocket是一种在单个TCP连接上进行全双工通讯的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
上面是维基百科的介绍. 简单的将,websocket 和http一样,都是一种网络传输协议
WebSocket 是独立的、创建在 TCP 上的协议。
Websocket 通过 HTTP/1.1 协议的101状态码进行握手。
为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”
那么websocket协议是如何握手的呢?
下面是websocket一次握手的过程 客户端请求
GET / HTTP/1.1Upgrade: websocketConnection: UpgradeHost: example.comOrigin: http://example.comSec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==Sec-WebSocket-Version: 13
服务器响应
HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=Sec-WebSocket-Location: ws://example.com/
和http字段不一样的地方
可以看到只是在http协议上增加了几个硬性规定,http协议的user-agent,cookie都可以在websocket握手过程中使用
抓包时候的注意事项:因为websocket只有一次握手,握手成功后就可以双方发送消息了,假如你打开网页后没有找到你要抓的数据,那么你就需要重新刷新网页,让他重新握手一次
表示刚刚连接的时候
表示收到消息怎么做
表示给服务器发送消息
表示关闭连接
那么知道了这些对我们有什么好处么? 找js的时候会很好找,这几个关键词基本上都是固定的 你可以直接全局搜搜,然后很容易能找到发送的js代码
模拟发送的时候也是一样的.
前面介绍了一堆websocket协议相关的东西,估计很多人已经晕了. 没关系,先看实例,有问题再回到上面看
我们先使用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}
,表示验证通过 然后我们投票,发送了投票的一些信息给服务 服务器告诉我们投票成功.
以上就是整个通讯过程.
那如果我们要模拟发送的话,需要知道哪些信息呢
{"action":"auth","val":5}
里面的 val:5
,这个5是固定的么?如果不是,是如何生成的{"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(或者onopen,onmessage,等等上面提到的事件去搜索)
运气很好,输入 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方法,大概作用是判断投票结果
所有的方法我们都找到了,那么我们再和之前要找的参数走一遍.
{"action":"auth","val":5}
里面的 val:5
,这个5是固定的么?如果不是,是如何生成的
这个5也就是onopen里面的authType,至于authType是不是固定的,搜索一下就知道了.eval(val)
得到的 所以你也可以直接执行这个.或者用python实现
{"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 websockettry: import threadexcept ImportError: import _thread as threadimport timedef 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
所以我们只要用我们上面得到的信息就行模拟发送就可以了