指南前言
为了帮助开发者更好且更灵活的接入抖音弹幕服务,我们特此开放出websocket私有化部署和API服务
接入的目的
在我们开发中常常因为业务不同需要对数据进行很对维度对计算使用,这里可以理解为业务越复杂就需要数据支持对颗粒度越细;而私有化部署和API服务就能解决好这些问题。
接入开发准备工作
- 申请私有化部署或API调用接口token 权限申请
1、websocket
API调用
调用地址: ws://{host}:{port}/dy
参数详细说明:
{host}
说明:如果是私有化部署请替换成你私有化部署服务器的IP,如果是官方平台websocket
API接口请替换成open.hperfect.cn
;port
说明:如果是私有化部署请填写你自己服务启动时候配置的地址,如果是官方平台websocket
API接口请替换成3000
连接成功后发送数据包 `连接成功后发一次就好了``
json
{
// 抖音直播地址 (必填参数)
"url": "https://live.douyin.com/xxxxxxx",
// web抖音登录后提取的cookie (必填参数)
"cookie": "登录后的抖音cookie"
}
{
// 抖音直播地址 (必填参数)
"url": "https://live.douyin.com/xxxxxxx",
// web抖音登录后提取的cookie (必填参数)
"cookie": "登录后的抖音cookie"
}
1.1、 抖音cookie提取
电脑浏览器打开抖音地址:https://live.douyin.com/ 进行登录后随便找个直播间进去 windows使用F12调出调试面板 如图提取cookie
1.2、在线调试如图
2、心跳包发送
为确保客户端长连接不掉线这里需要在每5分钟内发送一个心跳包,内容就是文本
ping
,以下是发送和接收Demo。
Python
import websocket
import threading
import time
def on_message(ws, message):
print("Received:", message)
def on_error(ws, error):
print("Error:", error)
def on_close(ws):
print("Closed")
def on_open(ws):
def run(*args):
while True:
ws.send("ping")
print("Sent 'ping' to server.")
time.sleep(120) # 120秒,即两分钟
threading.Thread(target=run).start()
if __name__ == "__main__":
# 服务器地址
server_addr = "ws://open.hperfect.cn:3000/dy"
# 连接WebSocket服务器
ws = websocket.WebSocketApp(server_addr,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.on_open = on_open
ws.run_forever()
import websocket
import threading
import time
def on_message(ws, message):
print("Received:", message)
def on_error(ws, error):
print("Error:", error)
def on_close(ws):
print("Closed")
def on_open(ws):
def run(*args):
while True:
ws.send("ping")
print("Sent 'ping' to server.")
time.sleep(120) # 120秒,即两分钟
threading.Thread(target=run).start()
if __name__ == "__main__":
# 服务器地址
server_addr = "ws://open.hperfect.cn:3000/dy"
# 连接WebSocket服务器
ws = websocket.WebSocketApp(server_addr,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.on_open = on_open
ws.run_forever()
Go
package main
import (
"log"
"net/url"
"time"
"github.com/gorilla/websocket"
)
func main() {
// 服务器地址
serverAddr := "open.hperfect.cn:3000/dy"
// 解析服务器地址
u := url.URL{Scheme: "ws", Host: serverAddr}
// 连接到WebSocket服务器
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
// 定期发送文本消息
for {
err := c.WriteMessage(websocket.TextMessage, []byte("ping"))
if err != nil {
log.Println("write:", err)
break
}
// 这里定义2分钟发一次
time.Sleep(120 * time.Second)
}
}
package main
import (
"log"
"net/url"
"time"
"github.com/gorilla/websocket"
)
func main() {
// 服务器地址
serverAddr := "open.hperfect.cn:3000/dy"
// 解析服务器地址
u := url.URL{Scheme: "ws", Host: serverAddr}
// 连接到WebSocket服务器
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
// 定期发送文本消息
for {
err := c.WriteMessage(websocket.TextMessage, []byte("ping"))
if err != nil {
log.Println("write:", err)
break
}
// 这里定义2分钟发一次
time.Sleep(120 * time.Second)
}
}
PHP
<?php
require __DIR__ . '/vendor/autoload.php';
use React\EventLoop\Factory;
use React\Socket\Connector;
use Clue\React\WebSocket\Client;
// 服务器地址
$serverAddr = 'ws://open.hperfect.cn:3000/dy';
// 创建事件循环
$loop = Factory::create();
// 创建 WebSocket 客户端
$connector = new Connector($loop);
$connector->connect($serverAddr)->then(function ($conn) use ($loop) {
echo "Connected to server\n";
// 定期发送 ping 消息
$loop->addPeriodicTimer(120, function () use ($conn) {
$conn->send('ping');
echo "Sent 'ping' to server\n";
});
// 接收消息
$conn->on('message', function ($message) {
echo "Received: {$message}\n";
});
}, function ($e) {
echo "Error: {$e->getMessage()}\n";
});
// 运行事件循环
$loop->run();
<?php
require __DIR__ . '/vendor/autoload.php';
use React\EventLoop\Factory;
use React\Socket\Connector;
use Clue\React\WebSocket\Client;
// 服务器地址
$serverAddr = 'ws://open.hperfect.cn:3000/dy';
// 创建事件循环
$loop = Factory::create();
// 创建 WebSocket 客户端
$connector = new Connector($loop);
$connector->connect($serverAddr)->then(function ($conn) use ($loop) {
echo "Connected to server\n";
// 定期发送 ping 消息
$loop->addPeriodicTimer(120, function () use ($conn) {
$conn->send('ping');
echo "Sent 'ping' to server\n";
});
// 接收消息
$conn->on('message', function ($message) {
echo "Received: {$message}\n";
});
}, function ($e) {
echo "Error: {$e->getMessage()}\n";
});
// 运行事件循环
$loop->run();
JavaScript
// 服务器地址
const serverAddr = 'open.hperfect.cn:3000/dy';
// 创建 WebSocket 连接
const ws = new WebSocket('ws://' + serverAddr);
// 连接打开时的处理
ws.onopen = function() {
console.log('Connected to the WebSocket server.');
// 使用 setInterval 定期发送消息
setInterval(function() {
ws.send('ping');
console.log('Sent "ping" to the server.');
}, 120 * 1000); // 120 * 1000 毫秒,即2分钟
};
// 接收到消息时的处理
ws.onmessage = function(event) {
console.log('Received:', event.data);
};
// 出现错误时的处理
ws.onerror = function(error) {
console.log('WebSocket Error:', error);
};
// 连接关闭时的处理
ws.onclose = function() {
console.log('WebSocket connection closed.');
};
// 服务器地址
const serverAddr = 'open.hperfect.cn:3000/dy';
// 创建 WebSocket 连接
const ws = new WebSocket('ws://' + serverAddr);
// 连接打开时的处理
ws.onopen = function() {
console.log('Connected to the WebSocket server.');
// 使用 setInterval 定期发送消息
setInterval(function() {
ws.send('ping');
console.log('Sent "ping" to the server.');
}, 120 * 1000); // 120 * 1000 毫秒,即2分钟
};
// 接收到消息时的处理
ws.onmessage = function(event) {
console.log('Received:', event.data);
};
// 出现错误时的处理
ws.onerror = function(error) {
console.log('WebSocket Error:', error);
};
// 连接关闭时的处理
ws.onclose = function() {
console.log('WebSocket connection closed.');
};
Java
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.TimerTask;
public class WebSocketPingClient {
public static void main(String[] args) throws URISyntaxException {
// 服务器地址
String serverAddr = "ws://open.hperfect.cn:3000/dy";
// 创建WebSocket客户端
WebSocketClient client = new WebSocketClient(new URI(serverAddr)) {
@Override
public void onOpen(ServerHandshake handshakedata) {
System.out.println("Opened connection");
}
@Override
public void onMessage(String message) {
System.out.println("Received message: " + message);
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("Closed connection");
}
@Override
public void onError(Exception ex) {
System.out.println("Error: " + ex.getMessage());
}
};
// 尝试连接到服务器
client.connect();
// 定期发送ping消息
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (client.isOpen()) {
client.send("ping");
System.out.println("Sent 'ping' to the server.");
}
}
}, 0, 120 * 1000); // 0延迟,每120秒执行一次(即两分钟)
}
}
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.TimerTask;
public class WebSocketPingClient {
public static void main(String[] args) throws URISyntaxException {
// 服务器地址
String serverAddr = "ws://open.hperfect.cn:3000/dy";
// 创建WebSocket客户端
WebSocketClient client = new WebSocketClient(new URI(serverAddr)) {
@Override
public void onOpen(ServerHandshake handshakedata) {
System.out.println("Opened connection");
}
@Override
public void onMessage(String message) {
System.out.println("Received message: " + message);
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("Closed connection");
}
@Override
public void onError(Exception ex) {
System.out.println("Error: " + ex.getMessage());
}
};
// 尝试连接到服务器
client.connect();
// 定期发送ping消息
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (client.isOpen()) {
client.send("ping");
System.out.println("Sent 'ping' to the server.");
}
}
}, 0, 120 * 1000); // 0延迟,每120秒执行一次(即两分钟)
}
}
C#
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class WebSocketClient
{
private ClientWebSocket webSocket = null;
private Uri serverUri;
private Timer timer;
public WebSocketClient(string url)
{
serverUri = new Uri(url);
webSocket = new ClientWebSocket();
}
public async Task ConnectAsync()
{
await webSocket.ConnectAsync(serverUri, CancellationToken.None);
Console.WriteLine("Connected!");
// Start the timer to send a ping message every 120 seconds
timer = new Timer(SendPing, null, 0, 120000);
}
private async void SendPing(object state)
{
if (webSocket.State == WebSocketState.Open)
{
string pingMsg = "ping";
ArraySegment<byte> buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(pingMsg));
await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
Console.WriteLine("Sent ping to server.");
}
}
public async Task ReceiveAsync()
{
var buffer = new ArraySegment<byte>(new byte[2048]);
while (webSocket.State == WebSocketState.Open)
{
WebSocketReceiveResult result;
do
{
result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
var message = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
Console.WriteLine("Received message: " + message);
}
} while (!result.EndOfMessage);
}
}
public static async Task Main(string[] args)
{
var client = new WebSocketClient("ws://open.hperfect.cn:3000/dy");
await client.ConnectAsync();
await client.ReceiveAsync();
}
}
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class WebSocketClient
{
private ClientWebSocket webSocket = null;
private Uri serverUri;
private Timer timer;
public WebSocketClient(string url)
{
serverUri = new Uri(url);
webSocket = new ClientWebSocket();
}
public async Task ConnectAsync()
{
await webSocket.ConnectAsync(serverUri, CancellationToken.None);
Console.WriteLine("Connected!");
// Start the timer to send a ping message every 120 seconds
timer = new Timer(SendPing, null, 0, 120000);
}
private async void SendPing(object state)
{
if (webSocket.State == WebSocketState.Open)
{
string pingMsg = "ping";
ArraySegment<byte> buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(pingMsg));
await webSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
Console.WriteLine("Sent ping to server.");
}
}
public async Task ReceiveAsync()
{
var buffer = new ArraySegment<byte>(new byte[2048]);
while (webSocket.State == WebSocketState.Open)
{
WebSocketReceiveResult result;
do
{
result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
var message = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
Console.WriteLine("Received message: " + message);
}
} while (!result.EndOfMessage);
}
}
public static async Task Main(string[] args)
{
var client = new WebSocketClient("ws://open.hperfect.cn:3000/dy");
await client.ConnectAsync();
await client.ReceiveAsync();
}
}
服务配置
在服务启动前如果需要修改对外暴露端口以及多进程模式参数等,请参考服务配置进行配置。
字段说明
首次连接直播间成功
201
状态码代表首次连接直播间成功,可以用该代码来判断是否成功连接直播间201
只会在第一次连接成功后推送一次
消息
json
{
"code":201,
"msg":"连接服务器成功",
"result":null
}
{
"code":201,
"msg":"连接服务器成功",
"result":null
}
错误返回
json
{
"code":500,
"msg":"数据格式有误",
"result":null
}
{
"code":500,
"msg":"数据格式有误",
"result":null
}
返回正常弹幕数据
判断消息事件类型请从result.common.method进行判断, 对照 字段说明处理对应逻辑
json
{
"code":200,
"msg":"ok",
"result":{
"action":"1",
"anchorDisplayText":{
"defaultFormat":{
"color":"#b8ffffff",
"weight":400
},
"defaultPatter":"{0:user} 来了{1:string}",
"key":"live_room_enter_toast",
"piecesList":[
{
"format":{
"color":"#8CE7FF",
"weight":400
},
"type":true
}
]
},
"anchorEnterTipType":"18",
"common":{
"anchorFoldType":"1",
"anchorFoldTypeV2":"3",
"foldType":"1",
"isShowMsg":true,
"method":"WebcastMemberMessage",
"msgId":"7326846538058380083",
"priorityScore":"42000",
"roomId":"7326833666590591785"
},
"memberCount":"8074",
"publicAreaCommon":{
},
"user":{
"id":"1874023962651579",
"nickName":"用户9646959349344"
}
}
}
{
"code":200,
"msg":"ok",
"result":{
"action":"1",
"anchorDisplayText":{
"defaultFormat":{
"color":"#b8ffffff",
"weight":400
},
"defaultPatter":"{0:user} 来了{1:string}",
"key":"live_room_enter_toast",
"piecesList":[
{
"format":{
"color":"#8CE7FF",
"weight":400
},
"type":true
}
]
},
"anchorEnterTipType":"18",
"common":{
"anchorFoldType":"1",
"anchorFoldTypeV2":"3",
"foldType":"1",
"isShowMsg":true,
"method":"WebcastMemberMessage",
"msgId":"7326846538058380083",
"priorityScore":"42000",
"roomId":"7326833666590591785"
},
"memberCount":"8074",
"publicAreaCommon":{
},
"user":{
"id":"1874023962651579",
"nickName":"用户9646959349344"
}
}
}