之前喜欢在斗鱼看直播,后来在知乎上看到大神们爬取斗鱼TV弹幕,因为自己正在学python,所以也跃跃欲试。后来斗鱼在3月份发布了新的弹幕协议,那么今天就尝试用python获取斗鱼TV的房间弹幕。
新版协议地址:《斗鱼弹幕服务器第三方接入协议》
斗鱼TV弹幕采用了TCP协议传输。所以我们需要导入socket库,顺便贴出其余用到的库。
1 2 3 4 5
| import socket import sys import struct import time import threading
|
然后开始连接到斗鱼的弹幕服务器, 初学python,代码略渣。。。
创建socket
1 2 3 4 5 6
| try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print '创建socket成功!' except socket.error,e: print '创建socket失败!' sys.exit()
|
连接到远程服务器
1 2 3 4 5
| try: s.connect((get_host_ip('openbarrage.douyutv.com'), 8601)) print '已连接到弹幕服务器' except socket.error,e: print e
|
发送登录请求
1 2 3 4 5 6 7 8 9 10
| message = 'type@=loginreq/roomid@='+str(ROOMID)+'/0' try: s.send(getbyte(message)) reply = s.recv(4096) if 'loginres' in reply: print '登录成功' else: print '登录失败' except socket.error,e: print '登录失败'
|
加入房间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| message = 'type@=joingroup/rid@='+str(ROOMID)+'/gid@=-9999/0' try: s.send(getbyte(message)) s.settimeout(TIMEOUT) reply = s.recv(4096) if 'type@=error' in reply: print '加入房间'+str(ROOMID)+'失败,请检查房间是否在线!' else: t1 = threading.Thread(target=send_heartbeat, args=(s,)) t2 = threading.Thread(target=recv_sock, args=(s,)) t1.start() t2.start() print '加入房间'+str(ROOMID)+'成功' print('开始读取弹幕信息:') print('------------------------------------------------') except socket.error,e: print '加入房间'+str(ROOMID)+'失败!'
|
这里需要注意的是,发送数据的时候,需要根据斗鱼消息协议加入协议头,否则收不到回复。
斗鱼消息协议格式如上所示,其中字段说明如下:
1 2 3 4 5 6 7
| 消息长度:4 字节小端整数,表示整条消息(包括自身)长度(字节数)。 消息长度出现两遍,二者相同。 消息类型:2 字节小端整数,表示消息类型。取值如下: 689 客户端发送给弹幕服务器的文本格式数据 690 弹幕服务器发送给客户端的文本格式数据。 加密字段:暂时未用,默讣为 0。 保留字段:暂时未用,默讣为 0。
|
根据此协议写一个方法来给发送的数据部分加入协议头
1 2 3 4 5 6 7 8
| def getbyte(data): a = struct.pack('i', len(data)+8) b = struct.pack('h', 689) c = struct.pack('b', 0) return a*2+b+c*2+data
|
另外在上述第四步,我们开启了两个线程,一个用于接收弹幕,一个用于向斗鱼服务器发送心跳包,因为斗鱼为了减少不正常的连接浪费资源,所有需要连接向服务器发送心跳来保持连接。斗鱼的心跳间隔目前设置为45秒。所以我们需要每小于45秒就往服务器发送心跳包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def send_heartbeat(c_sock): while True: time.sleep(10) msg = 'type@=keeplive/tick@='+get_timestamp(get_datetime())+'/' c_sock.send(getbyte(message)) time.sleep(30)
def recv_sock(c_sock): while True: reply = c_sock.recv(4096) get_str(reply) time.sleep(0.5)
|
其中get_str()方法,是用来格式化收到的消息的,我写的拙劣,就不贴出来了。另外本人初学python,写的代码难免有不规范和不足之处,见谅!这样获取斗鱼TV弹幕的功能就基本实现了。
result
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 电竞小可爱小仙女 : 小宇宙爆发吧! 大旭我套你个猴 : 哈哈哈啊啊 旭哥哥人家还想要 : 给爸爸成儿子!!! 听闻杨卓璇 : 碎税睡 伊甸园之东 : 左边不慌,还想加仓 天虐老贼单身狗 : 。。 浪费青春c : 50块 巨款 模凡 : 卖无色、 旧城以西是离愁丶 : 成成宝宝 宝哥我想中两次奖 : 哈哈已经是碎了不用再垫了 海外散仙历飞雨 : 卖无色 猫九命阿 : 提前舒服 怎能忘了西遊 : 已经是碎了 苞米地穿西装 : 墨迹必碎 改名为套猴 : 看看你那个怂样 装B王丶MaeKMO : 墨迹必碎 九日九日梦想秀 : 成成成成成成成 Piubull : 。。 有很多人很多人很多 : 赞助必碎 我不是任怡旭 : 右边我都梭哈了 旭的吊大粗又黑 : 墨迹必碎 99999改名字一定会中奖 : 一定不要成 丨尚安哥哥丨 : 上16化粪池蝶泳 Smile丶杨先森 : 碎 泪寒i28861 : 理财 炫迈牌套套XXXXL号 : 人格担保 莪愿一人 : 无色换成矛盾卖了就行了 暴打小佳佳 : 墨迹必碎 HAC丶无聊抽抽 : 左边的拉了 凌昕i : 成功我吃屎 记住我的ID 3 恭喜我这个逼中奖了 : 给爸爸碎 一点点兮欢 : 墨迹已经废了 何必灬老友 : ????? 斯亻独憔悴 : 没有秘药 401丶Torres : 基本上碎了,虽然我压的左边 在妓院的和尚 : 卖无色啊 A余咏记 : 有人要上天台了
|