专栏首页Python爬虫与算法进阶WebSocket爬虫之爬取龙珠弹幕

WebSocket爬虫之爬取龙珠弹幕

我是个宅男,喜欢看很多人直播,以前可以看一天直播不出门。现在主要看这么些主播,虎牙的韦神、Dopa,斗鱼的狗贼嘘嘘。

对于其中的弹幕文化,非常感兴趣,就研究下,发现弹幕是用WebSocket实现的,那首先来说说什么是WebSocket。

WebSocket是什么

详细内容可以看看这个问题 https://www.zhihu.com/question/20215561

简单解释下:

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。大多数 Web 应用程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。

WebSocket 如何工作

一个非常典型的WebSocket创建方式如下(来自某巨头):

function r() {
    if (!d) {
        var t = i();
        I.info("%cconnecting " + t, p("#0000E3")),
            u = new WebSocket(t),
            u.onopen = n,
            u.onclose = o,
            u.onerror = a,
            u.onmessage = h
    }
}

WebSocket获取龙珠直播弹幕

本次使用的Python第三方库是 https://github.com/websocket-client/websocket-client

看看官方例子:

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()

是不是非常熟悉,和上面讲到的一模一样,4种主要思想方法都是一致的,可以直接调用。

那么到了实践环节,本次选取的是龙珠直播,为啥不是虎牙、斗鱼呢?这个待会再说,我们打开龙珠某个直播间

在网络里面选择ws这一项,即可看到相关连接,而且这些消息是加密过的,别急,我们打开m站试试

这个时候传输的弹幕消息已经没有加密过,直接对比,看到了一条“哈哈哈”的消息,所以我们现在可以确定就是这个websocket连接在传输相关消息。

依葫芦画瓢,我们尝试用Python来连接

Curl:

curl 'wss://mbgows.plu.cn:8806/?room_id=2185&group=0' -H 'Pragma: no-cache' -H 'Origin: http://m.longzhu.com' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7' -H 'Sec-WebSocket-Key: n72+EfLt2iSrQ0EswTZ+2A==' -H 'User-Agent: Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Mobile Safari/537.36' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits' -H 'Cache-Control: no-cache' -H 'Cookie: pluguest=81D781D68480BE065D952CA699B38E6627B61756AEF57338B39053154850A9502BC7FD850F86922BDF3DBD7F774BFDE5CBC80838A34B8F26' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' --compressed

Python代码

#!/usr/bin/env python  
# -*- coding: utf-8 -*-
"""
@author: zhangslob
@file: longzhu_websocket.py 
@time: 2018/11/17
@desc: simple websocket client to connect longzhu
"""

import websocket
try:
    import thread
except ImportError:
    import _thread as thread
import time


def on_message(ws, message):
    import json
    try:
        print(json.loads(message))
    except:
        print(message)


def on_error(ws, error):
    print(error)


def on_close(ws):
    print("### closed ###")


def on_open(ws):
    pass
    # 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, ())


headers = {
    'Pragma': 'no-cache',
    'Origin': 'http://m.longzhu.com',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
    # 'Sec-WebSocket-Key': 'n72+EfLt2iSrQ0EswTZ+2A==',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Mobile Safari/537.36',
    'Upgrade': 'websocket',
    'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
    'Cache-Control': 'no-cache',
    'Connection': 'Upgrade',
    'Sec-WebSocket-Version': '13',
}


if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://mbgows.plu.cn:8806/?room_id=2185&group=0",
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close,
                                header=headers)
    ws.on_open = on_open
    ws.run_forever()

你可以直接运行上面的代码,看看会有什么结果

龙珠直播这个有点奇怪,你根本不用去向服务器发送什么消息,服务器无脑给你推送弹幕,常见的是客户端需要先告诉服务器“我是谁”,然后服务器再推送消息给你,并且还有有心跳检测,“我”告诉服务器我还在看呢,你继续给我弹幕,看看虎牙和斗鱼。

图中绿色的是发送的消息,红色是接受的消息。像这种情况就需要自己去看js代码是如何处理消息的。斗鱼的话有公开自己的弹幕服务器第三方接入协议。

copy代码可以阅读原文

本文分享自微信公众号 - Python爬虫与算法进阶(zhangslob)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-11-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 解释一下 HashMap 的工作原理

    HashMap 是基于散列表的数据结构。所谓散列表,它通过键值对的方式存储数据,把 key 通过散列算法计算出一个存储地址,将 value 放入这个地址中。散列...

    水货程序员
  • volatile 解决了什么问题?

    volatile 关键字是最常问到的问题,关于这个关键字的作用解释,网上的文章已经多如牛毛了。

    水货程序员
  • Java 内存模型简述

    Java 内存在逻辑功能上分成 5 个区。方法区,堆区,JVM 栈,方法栈,程序计数器(PC 寄存器)。

    水货程序员
  • Hanlp分词实例:Java实现TFIDF算法

    关于TFIDF算法的介绍可以参考这篇博客http://www.ruanyifeng.com/blog/2013/03/tf-idf.html。

    IT小白龙
  • 微信团队分享:Kotlin渐被认可,Android版微信的技术尝鲜之旅

    本文由微信开发团队工程是由“oneliang”原创发表于WeMobileDev公众号,内容稍有改动。

    JackJiang
  • Maven 的 Pom 中,dependencymanagement 和 dependencies 区别是什么(面试常问)

    dependencyManagement 统一了 maven 中依赖的版本号,定义在 dependencies 中的依赖,在不指定具体版本号时,就会沿着上层找到...

    水货程序员
  • JVM 线程和操作系统线程一一对应吗?

    Java 的线程和操作系统的线程是一一对应的。使用 Java 线程就是使用一个操作系统本地线程。

    水货程序员
  • 深拷贝和浅拷贝的区别是什么,如何实现?

    深拷贝是彻底的拷贝,两对象中所有的成员都是独立的一份,而且,成员对象中的成员对象也是独立一份。

    水货程序员
  • IE中的unknown类型

    如果在type这段script内有vbTest的JavaScript函数声明,那么typeof得到的将会是function,vbscript中的vbTest将会...

    meteoric
  • JavaScript 学习总结

    Java和Javascript的区别就像印度和印度尼西亚的区别,名字中有点相同的东西。

    三杯水Plus

扫码关注云+社区

领取腾讯云代金券