1. WebSocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。,被广泛应用于对数据实时性要求较高的场景,如体育赛事播报、股票走势分析、在线聊天等。
在WebSocket协议未出现之前,很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯
2. WebSocket握手协议
与HTTP协议不同的是,WebSocket协议只需要发送一次连接请求。请求的完整过程被称为握手,即客户端为了创建WebSocket连接而向服务器发送特定的HTTp请求并声明升级协议。WebSocket 是独立的、创建在 TCP 上的协议,通过HTTP/1.1 协议的101状态码进行握手,握手成功后才会转为WebSocket协议。
WebSocket通信过程:
客户端发起握手请求 服务器端收到请求后验证并返回握手结果 连接建立成功,服务器端开始推送消息3. WebSocket爬虫实战
1.爬虫目标
本次抓取的目标网站是乐鱼体育的足球比分页,该页面会实时展示比分更新数据。
2.网页分析
1.打开开发者模式,我们检查网页源代码会发现,源代码中并没有我们想要的比分数据。
2. 那么是否是使用AJAX请求来更新数据?但是通过观察,比分一直在更新但并没有新的AJAX请求发出。说明该网站并不是使用轮询的方式更新数据。
3. 所以,该网站数据的实时更新应该是使用websocket协议。我们点开【WS】,会发现服务器一直在传输新的信息。这些信息就是我们需要的数据。
4. 将这条请求翻到最上方,我们会看见三条客户端请求服务器端的绿色请求信息,也就是验证信息message。并且经过重复刷新观察,只有其中一条请求有参数变化,且其参数是毫秒级的时间戳,需要我们手动构造。
5. 查看该条请求的url,会发现其路由地址包含一串加密字符串token。通过寻找发现在请求过程中,有这样一条AJAX请求恰好返回了该token。
6. 现在所有的参数我们都已经直接或间接获取到。这样我们就可以着手开始使用代码模拟websocket请求获取数据了。
3.代码实现
请注意:
引入的websocket模块并不是需要下载安装websocket模块(pip install websocket)。而是需要下载安装websocket-client模块。如果你下载了websocket,请卸载!!!
import requests
import websocket
import json
import time
import math
def getToken():
"""
获取加密字符串,将其拼接到websocket协议的url上
:return: token
"""
url = "https://live.611.com/Live/GetToken"
response = requests.get(url)
if response.status_code == 200:
data = json.loads(response.text)
token = data["Data"]
return token
else:
print("请求错误")
def get_message():
"""
构造websocket的验证信息
:return: message1,message2
"""
_time = math.floor(time.time()) * 1000
info = {'chrome': 'true', 'version': '80.0.3987.122', 'webkit': 'true'}
message1 = {
"command": "RegisterInfo",
"action": "Web",
"ids": [],
"UserInfo": {
"Version": str([_time]) + json.dumps(info),
"Url": "https://live.611.com/zq"
}
}
message2 = {
"command": "JoinGroup",
"action": "SoccerLiveOdd",
"ids": []
}
return json.dumps(message1), json.dumps(message2)
def Download(token,message1,message2):
"""
抓取数据
:param token: token
:param message1: message1
:param message2: message2
:return:
"""
uri = "wss://push.611.com:6119/{}".format(token)
ws = websocket.create_connection(uri, timeout=10)
ws.send(message1)
ws.send(message2)
while True:
result = ws.recv()
print(result)
if __name__ == '__main__':
token = getToken() # 获取token字符串
message1, message2 = get_message() # 构造请求信息
Download(token,message1, message2) # 抓取数据
参考资料:菜鸟教程
思路来源:《Python3 反爬虫原理与绕过实战》---- 韦世东 著