本篇文章我们来写一下斗鱼弹幕爬虫的Java版。可能有人会说Python版本的爬虫到处都可以搜到,而且用Python的scrapy、beautifulsoup等库又快又方便,那么为什么我们还要用Java写爬虫呢?
事实是这样没错,但是这次的弹幕爬虫严格来讲主要涉及到网络数据传输,并不需要构造html标签,直接用socket + mybatis请求到数据后存入数据库即可(我会说是python的多线程不熟悉吗),其实也很方便,也不用去加脚本加py文件来实现。总而言之就两个字,简单。
由于头条的编辑器对于写代码不友好,因此我都用图片进行表示,需要源码的同学可以到文末的github链接上下载。
下载斗鱼协议文档
斗鱼弹幕爬虫原理很简单:
- 客户端依次发送,登陆请求、加入房间请求、加入弹幕组请求。
- 斗鱼的服务端返回弹幕
当然由于是一个长链接,还需要另起一个心跳线程(heart thread)来不断发送alive消息,以保持连接。
Step1 下载协议文档
去斗鱼第三方开放平台下载两份文档:
- 《斗鱼第三方开放平台API文档v2.2》
- 《斗鱼弹幕服务器第三方接入协议v1.6.2》
斗鱼第三方地址:http://dev-bbs.douyutv.com/forum.php?mod=forumdisplay&fid=37
Step2 了解协议
根据第三方接入协议所述,我们需要按照斗鱼消息协议格式向斗鱼的弹幕服务器发送请求。
发送消息
就像TCP的3次握手一样,首先由客户端发出第一次握手,我们需要先发出登陆请求,为了扩展,我们将所有请求都认为是msg,先写一个请求函数。
我们定义一个发送请求的函数,这里的client就是我们的连接,msg就是我们具体的请求类型。学过计算机网络的应该知道,两个网络节点之间的交互是通过套接字来进行的,而Java中的Socket类就可以被作为网络中的套接字,client将会通过以下方式进行初始化:
Socket client = new Sokcet("openbarrage.douyutv.com", "8601");
指定弹幕服务器与端口号。在我的电脑上不知道为什么在请求openbarrage.douyutv.com时会报:Unknow Host。因此我直接使用它的ip来初始化Socket。
我们在命令行ping一下openbarrage.douyutv.com,就可以看到它的ip地址:119.96.201.28
因此我采用的初始化方式为
Socket client = new Sokcet("119.96.201.28", "8601");
然后我们看如何构造斗鱼的请求头。
可以看到,该请求头一共是 4 字节的长度 + 8 字节的头部 + 数据部。那么表格中的消息长度是多少呢?
消息长度 = 头部 + 数据部
这里将消息长度这个信息重复了两次,但是第一次不算在长度的计算之中,所以消息长度为 8 + 数据的长度 + 1(结尾的'\0')。即9 + 数据长度。
我们需要将数据分5块,分别是4字节消息长度,4字节消息长度,4字节的消息类型+加密字段+保留字段,数据部和最后一字节的结尾符(0代表'\0'的字节形式),写入byte数组输出流中。
这些数据都需要转成字节形式,再看斗鱼协议,里面说斗鱼的消息格式都是小端模式(不像字符串有String.getBytes()函数,int得自己定义了)。我们定义两个转换函数:
解释一下这个函数,int是4字节32位的,将data右移24位后,左边补0,得到最左边的8位(位于最右边)。此时通过与上0xFF,即与上0000 0000 0000 0000 0000 0000 1111 1111。将左边24位都清0,留下右边8位,存在byte数组中。
接受消息
在接受消息时,先拿到返回消息的长度,以便在下面申请buffer的时候不需要申请多余的buffer,然后将几个无用数据(都是4字节)都读出来。
消息长度-8就是真正的数据返回长度。通过循环读入,直到读入的数据长度等于斗鱼告诉我们的消息长度为止。
爬虫线程
然后我们来写一个爬虫线程类。
首先发送登陆请求与加入弹幕组请求:
然后在连接到斗鱼后,开始处理消息:
这里的消息处理函数并非像Python那样通过正则来取,而是:
将消息用'/'划分,然后另key为@=前面的值,value为@=后面的值。
心跳线程
主函数
创建一个主函数类后,直接用psmv一键生成public static void main。
以下是老陈和肚皮怪的弹幕:
当然,后续将会用线程池优化一下,事实上,上面的代码已经加入了springboot与mysql了。想要源码的同学可以去github上下载:https://github.com/9plus/As/tree/master/demo/DyTest。