Python学习-通过斗鱼api获取弹幕

最近在看廖雪峰老师的python教程,于是乎找点事干练练手。

在网上找到一份《斗鱼弹幕服务器第三方接入协议v1.6.2》,有了api就方便多了,不需要抓包分析。
##协议组成
Python学习-通过斗鱼api获取弹幕_第1张图片
从文档内可以找到协议头的组成
新建一个DataPackage类来包装信息

class DataPackage(object):
    tag = ('type','roomid','tick','rid','gid')
    def __init__(self, code, **kw):
        self.dict = {}
        self.reqcode = code
        self.dict.update(kw)
        
    def __getattr__(self, item):
        if item in self.dict:
            return self.dict[item]

其中dict来保存数据。
可以看到2个消息长度是8byte,消息类型、加密字段、保留字段分别是2byte、1byte、1byte,那么加起来就是12byte

    def pack(self):
        data = ''
        for k, v in self.dict.items():
            data = data + k + '@=' + v + '/'
        data = data.encode('utf-8') + b'\0'
        data_length = len(data) + 12
        
        header = struct.pack(', data_length) + struct.pack(', data_length) + struct.pack(',self.reqcode) \
        + struct.pack(', 0) + struct.pack(', 0)
        #print(len(header))输出:12
        
        #后面查找错误用
        #print('send:' + self.__str__())
        return header + data

由于采用小端编码,所以可以改成

header = struct.pack('

发送消息

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('openbarrage.douyutv.com', 8601))


def main(roomid):
    login(s, roomid)
    while True:
        d = s.recv(1024)
        if d:
            package = DataPackage(690)
            #解析数据
            package.form(d)
            #处理信息
            handle_msg(s, package)
        else:
            break


def login(s, roomid):
    data = DataPackage(689, type='loginreq', roomid=roomid).pack()
    s.send(data)
    data = DataPackage(689, type='joingroup', rid=roomid, gid='-9999').pack()
    s.send(data)

在发送’loginreq’之后,服务器并没有按照协议返回数据,害我还以为哪里出了问题。其实只要再发送’joingroup’就可以拿到成功信息了。
##处理信息
正好练习一下正则表达式

attr = re.compile(rb'(\w+)@=(.*?)/')

    def form(self, data):
        attrs = DataPackage.attr.findall(data)
        for a in attrs:
            self.dict[a[0].decode('utf-8')] = a[1].decode('utf-8')

接着处理解析好的数据

def handle_msg(s, data_package):
    #后面查找错误用
    #print('receive:' + data_package.__str__())
    typ = data_package.type
    if typ == 'chatmsg':
        print("[%s]%s"%(data_package.nn,data_package.txt))

'data_package.nn’是用户昵称,'data_package.txt’则是弹幕内容
##发送心跳包
每40s发送一次心跳包,防止掉线

def keep_alive():
    data = DataPackage(689, type='mrkl').pack()
    s.send(data)
    time.sleep(40)

运行测试

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=main, args=('30191',))
    p2 = multiprocessing.Process(target=keep_alive)
    p1.start()
    p2.start()

接着就出问题了T-T,没有弹幕输出。

试着把信息打印出来

class DataPackage(object):
    def __str__(self):
        s = ''
        for k, v in self.dict.items():
            s = s + k + '=' + v + ';'
        return s

输出如下:

send:type=loginreq;roomid=30191;
send:type=joingroup;rid=30191;gid=-9999;
send:type=mrkl;
receive:type=loginres;userid=0;roomgroup=0;pg=0;sessionid=0;username=;nickname=;live_stat=0;is_illegal=0;ill_ct=;ill_ts=0;now=0;ps=0;es=0;it=0;its=0;npv=0;best_dlev=0;cur_lev=0;nrc=3758096520;ih=0;sid=70143;sahf=0;sceneid=0;
receive:type=pingreq;tick=1533054218508;

等等!pingreq是什么鬼!文档里面没有这个东西!
那…我只好试试用’pingres’回应,输出如下:

send:type=loginreq;roomid=30191;
send:type=joingroup;rid=30191;gid=-9999;
send:type=pingres;tick=1533055273438;
send:type=mrkl;
receive:type=error;code=51;

emm…Python学习-通过斗鱼api获取弹幕_第2张图片
弄了很久都没有解决这个问题,网上也搜索不到对’pingreq’的处理,但我发现rieuse的代码里是这样写的

data_length = len(data) + 8

遂跟着修改代码,成功取到弹幕

寻找错误原因

header的长度确实是12byte,到底是哪里出了问题呢?

找了很久,在知乎上找到一名匿名用户的回答(部分截取如下)
Python学习-通过斗鱼api获取弹幕_第3张图片
那不应该是+12么???等我再搜一下,以后补上

##题外话
我也尝试过抓包。chrome的开发者工具抓不到,只好用wireshark。结果
Python学习-通过斗鱼api获取弹幕_第4张图片

完整代码:https://github.com/Chgtaxihe/DouyuDanmu

第一次写博客,不当之处还望指正。

2019.02.13更新

发送心跳包那段代码需要加个循环,不然进程跑完就关掉了。
感谢@a710463885指出

你可能感兴趣的:(python学习)