Python—WebSocket爬虫实战

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通信过程:

  1. 客户端发起握手请求
  2. 服务器端收到请求后验证并返回握手结果
  3. 连接建立成功,服务器端开始推送消息

3. WebSocket爬虫实战

1.爬虫目标

  本次抓取的目标网站是乐鱼体育的足球比分页,该页面会实时展示比分更新数据。

Python—WebSocket爬虫实战_第1张图片

2.网页分析

1.打开开发者模式,我们检查网页源代码会发现,源代码中并没有我们想要的比分数据。

Python—WebSocket爬虫实战_第2张图片

2. 那么是否是使用AJAX请求来更新数据?但是通过观察,比分一直在更新但并没有新的AJAX请求发出。说明该网站并不是使用轮询的方式更新数据。

Python—WebSocket爬虫实战_第3张图片

3. 所以,该网站数据的实时更新应该是使用websocket协议。我们点开【WS】,会发现服务器一直在传输新的信息。这些信息就是我们需要的数据。

Python—WebSocket爬虫实战_第4张图片

4. 将这条请求翻到最上方,我们会看见三条客户端请求服务器端的绿色请求信息,也就是验证信息message。并且经过重复刷新观察,只有其中一条请求有参数变化,且其参数是毫秒级的时间戳,需要我们手动构造。

Python—WebSocket爬虫实战_第5张图片

5. 查看该条请求的url,会发现其路由地址包含一串加密字符串token。通过寻找发现在请求过程中,有这样一条AJAX请求恰好返回了该token。

Python—WebSocket爬虫实战_第6张图片
Python—WebSocket爬虫实战_第7张图片

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 反爬虫原理与绕过实战》---- 韦世东 著

你可能感兴趣的:(爬虫,Python)