みなさんはYoutubeのライブ配信を見てますでしょうか?配信してますでしょうか?
うちは見たり、配信したりしてます。(うちの配信は↓から見れますので!)
配信してたりすると、たまに気になりませんか?
みんなから貰えるコメント、
なにかに利用(悪用)できないかな……?🤔🤔
うちは気になります!!!!
そして配信者さんなら、すぐにピンとくるでしょう。「わんコメ」さん、という滅茶苦茶便利なコメントアプリを。
それもアリだけど、プログラマーならこう思うかも知れません。
せっかくなら自作して、
自分がやりたい仕組みを作りたくない……?🤔🤔
ということで、今回は『自分の力(検索力)』で『Youtubeライブ』の『コメントを取得』して『あれこれする』を目指します💪🏻💪🏻
🧂下準備
さて、Youtubeライブのコメントってそもそもどうやって取得したらいいんだろう。と思ったので、一番使い慣れてる Node.js でライブラリがないか調べてみました。
調べ方は簡単です。 npm でkeywords:youtube keywords:live keywords:chat
と力技で検索するだけです。
検索したところ、以下のライブラリがでてきました!
youtubei
これが一番人気っぽい。scrape-yt というライブラリがあり、古くなったので代わりに登場したライブラリみたいです。ドキュメントも豊富なので使いやすそう。
ライブ配信のチャットを取得する方法も載ってますね。
Tubechat
こちらはシンプルで、ライブ配信のチャット(コメント)を拾うだけみたいですね。最終更新は24年1月なので古くはない。
ただ、シンプル過ぎる故に使い所が難しいかも?あとドキュメントもないのでコレ以上の機能はなさそう?
youtube-chat
こっちも同じくライブ配信のチャットを拾うだけみたいです。ただ最終更新が22年12月なので使い続けるのは不安。
ちなみに、作者さんはVTuberさんみたいです!日本語でライブラリの解説記事がありますので、初めて触るにはいいかもしれませんね。
その他
その他に見つけたのは youtubei や youtube-chat の派生っぽい感じがする……
🍳調理してみる
実際にライブラリを触ってみましょう。
youtubei
まずはインストールします。
npm i youtubei
そしてライブ配信のチャットを取得する方法が載ってましたので、試して見ましょう。どうやら、このコードはライブ配信のIDを利用するみたいですね。
const { Client } = require('youtubei')
const youtube = new Client()
/**
* ライブ配信のチャットを取得する関数
* @param {string} videoId - ライブ配信のID
*/
const run = async videoId => {
const video = await youtube.getVideo(videoId)
video.on('chat', chat => {
console.log(`${chat.author.name}: ${chat.message}`)
})
video.playChat(5000)
}
// ライブ配信のID(https://www.youtube.com/watch?v=LIVE_VIDEO_ID)を記入
const videoId = 'LIVE_VIDEO_ID'
run(videoId)
問題無く取得することができましたね!ちなみにプレミア公開のライブチャットも取得できました。使う事あるかなあ??
chat
で取得できる内容は下記に纏まっています。
ライブ配信が始まってない、もしくは終了してる場合
エラーなります。
C:\GitHub\AdventCalendar2024\youtubei.js:10
video.on('chat', chat => {
^
TypeError: video.on is not a function
at run (C:\GitHub\AdventCalendar2024\youtubei.js:10:9)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
悩みどころ
ドキュメントを見る限り、チャットで独自の絵文字が送信された場合に反映されないっぽい。なのでYouTubeで見えるコメントと、プログラムで取得したコメントに差異が生まれてしまいそうなので、チョット……ってなる。
あと非推奨のモジュールが存在するっぽいので、エラーを吐いてます。むむっ!
[DEP0040] DeprecationWarning: The punycode module is deprecated. Please use a userland alternative instead.
(Use node --trace-deprecation ... to show where the warning was created)
Tubechat
こちらも、まずはインストールします。
npm i tubechat
そしてライブ配信のチャットを取得する方法がこれも載っていますので、試して見ましょう。こっちはライブ配信をしているチャンネルのハンドルを利用するみたいです(チャンネルのIDでは動かないので注意!)。
const { TubeChat } = require('tubechat')
const tubeChat = new TubeChat()
/**
* ライブ配信のチャットを取得する関数
* @param {string} handleId - チャンネルのハンドル
*/
const run = handleId => {
tubeChat.connect(handleId)
tubeChat.on('message', ({ badges, channel, channelId, color, id, isMembership, isModerator, isNewMember, isOwner, isVerified, message, name, thumbnail, timestamp }) => {
console.log(channel, name, message)
})
}
// チャンネルのハンドル(https://www.youtube.com/@HANDLE_ID)を記入
const handleId = 'HANDLE_ID'
run(handleId)
こちらも問題なく取得できました!
ライブ配信が始まってない、もしくは終了してる場合
なにも起きません。エラーすら吐きません。
これ、ライブ配信が始まったら自動的に発火するのかな?そのあたりの挙動は(時間が無かったので)一切調べてないです。もし知ってる方がいたら、情報ください!!!
悩みどころ
まず配信が始まってるか否か判断できないのはツラい。自動的に始まるならアリ!って感じ。
あとmessage
がArray<{text?: string, emoji?: string}>
なので調整してあげる必要があるかも。
[
{ text: 'これはテスト' },
{ emoji: 'https://yt3.ggpht.com/......' }
]
youtube-chat
こちらも、まずはインストールします。
npm i youtube-chat
そしてライブ配信のチャットを取得する方法がこれも載っていますので、試して見ましょう。こちらはライブ配信のIDでも、チャンネルのIDでも取得できるようです。ただしハンドルを利用した場合はRequest failed with status code 404
とエラーが返ってきてしまったので、ID専用と考えるのがよさそうです。
const { LiveChat } = require('youtube-chat')
/**
* ライブ配信のチャットを取得する関数
* @param {string} liveId - ライブ配信のID
*/
const run = liveId => {
const liveChat = new LiveChat({ liveId })
liveChat.on('chat', chatItem => {
console.log(chatItem)
})
liveChat.on('error', err => {
console.log(err.message)
})
liveChat.start()
}
// ライブ配信のID(https://www.youtube.com/watch?v=LIVE_VIDEO_ID)を記入
const liveId = 'LIVE_VIDEO_ID'
run(liveId)
無事、取得することができました!
取得した型については長いので、以下を参考にしてください。いろいろな情報があるので便利そう。
ライブ配信が始まってない、もしくは終了してる場合
エラーなります。
Continuation was not found
悩みどころ
ハンドルで取得できないのか…… そこがあれば完璧だったと思います。
🤔さて、どれを使う?
youtubei は独自の絵文字が取得できない……
Tubechat は開始してるのか否か判断つかない……
youtube-chat はハンドルから取得できない……
どれも問題を抱えてますので、うちが導き出した答えは……
ライブ配信のIDが分からない場合は、youtubei で取得する!そしてライブ配信のIDを利用して、Tubechat または youtube-chat でチャットを監視すればええのだ!!
でも今回は面倒くさいのでチャンネルのIDを利用して youtube-chat でチャットを監視するのだ!!
という結論になりました。youtubei で絵文字も取得できる方法があれば一発なんだけどね……
youtubei × youtube-chat
まずはライブ配信のIDを取得する方法を考えます。幸いなことに youtubei のドキュメントにて、ライブ配信の一覧を取得する方法が載ってますので、まずはコレを利用します。
const { Client } = require('youtubei')
const youtube = new Client()
/**
* チャンネルのハンドルから、チャンネルのIDを取得する関数
* @param {string} handleId - チャンネルのハンドル
*/
const run = async handleId => {
const channel = await youtube.findOne(handleId, {type: 'channel'})
if (channel == null) {
console.log('チャンネルが見つかりませんでした')
return
}
console.log(channel.id)
}
// チャンネルのハンドル(https://www.youtube.com/@HANDLE_ID)を記入
const handleId = 'HANDLE_ID'
run(handleId)
これでチャンネルのハンドルから、チャンネルのIDが取得できました。あとは youtube-chat に渡してあげるだけですね。
const { Client } = require('youtubei')
const { LiveChat } = require('youtube-chat')
const youtube = new Client()
/**
* チャンネルのハンドルから、ライブ配信のチャットを取得する関数
* @param {string} handleId - チャンネルのハンドル
*/
const run = async handleId => {
const channel = await youtube.findOne(handleId, {type: 'channel'})
if (channel == null) {
console.log('チャンネルが見つかりませんでした')
return
}
const liveChat = new LiveChat({ channelId: channel.id })
liveChat.on('chat', chatItem => {
console.log(chatItem)
})
liveChat.on('error', err => {
console.log(err.message)
})
liveChat.start()
}
// チャンネルのハンドル(https://www.youtube.com/@HANDLE_ID)を記入
const handleId = 'HANDLE_ID'
run(handleId)
ただコレでは読みにくいので、ログを加工してみましょう!今回は 日時 [ユーザ名] コメント
というログを表示させてみます。コマンドプロンプトじゃない画面なら絵文字とかも見える様にしてあげるとベストかも!
const { Client } = require('youtubei')
const { LiveChat } = require('youtube-chat')
const youtube = new Client()
/**
* ゼロパディング関数
* @param {number} num - 日時の数値
* @returns {string} - ゼロパディングされた日時のテキスト
*/
const zeroPadding = num => {
const str = String(num)
return (str.length == 1) ? `0${str}` : str
}
/**
* Date型をJSTに変換する関数
* @param {Date} date - Date型のオブジェクト
* @returns {string} JST形式の日時(YYYY/MM/DD HH:mm:ss)
*/
const convertToJST = date => {
const Y = date.getFullYear()
const M = zeroPadding(date.getMonth() + 1)
const D = zeroPadding(date.getDate())
const h = zeroPadding(date.getHours())
const m = zeroPadding(date.getMinutes())
const s = zeroPadding(date.getSeconds())
return `${Y}/${M}/${D} ${h}:${m}:${s}`
}
/**
* messageをstringに変換する関数
* @param {Array<{text?: string, url?: string, alt?: string, isCustomEmoji?: boolean, emojiText?: string}>} message - テキストまたは絵文字データの配列
* @returns {string} - スペースで区切られたテキスト
*/
const messageJoin = message => {
// messageを配列にする
const array = message.map(object => {
const { text, url, alt, isCustomEmoji, emojiText } = object
// テキストの場合はtextを返す
if (text) return text
// isCustomEmojiがtrueの場合はurlを利用して画像を見せるのが良い
// コマンドプロンプトでは画像を見ることができないためaltを返す
if (isCustomEmoji) return alt
// いずれにも一致しないのは絵文字のみ
return emojiText
})
// スペース区切りのstringにする
return array.join(' ')
}
/**
* チャンネルのハンドルから、ライブ配信のチャットを取得する関数
* @param {string} handleId - チャンネルのハンドル
*/
const run = async handleId => {
const channel = await youtube.findOne(handleId, { type: 'channel' })
if (channel == null) {
console.log('チャンネルが見つかりませんでした')
return
}
const liveChat = new LiveChat({ channelId: channel.id })
liveChat.on('chat', chatItem => {
const { author, message, timestamp } = chatItem
const { name } = author
const text = messageJoin(message)
const date = convertToJST(timestamp)
console.log(`${date} [${name}] ${text}`)
})
liveChat.on('error', err => {
console.log(err.message)
})
liveChat.start()
}
// チャンネルのハンドル(https://www.youtube.com/@HANDLE_ID)を記入
const handleId = 'HANDLE_ID'
run(handleId)
これで良い感じにチャットログを追えるようになりましたね!
注意点
- youtubei で検索しているので必ずしも欲しいチャンネルではないケースがあるので選択させる必要がある
- 今回のコードは1つのチャンネルしか返さないため注意。解決策は後述
- 複数ライブ配信が同時に行われている場合は選択させる必要がある
- こちらの解決策も後述
- ハンドルだけでなくチャンネルIDまたはライブ配信IDでもコメントを取得できるようにする必要がある
- 今回はコマンドプロンプトなので割愛(readlineを使えばできる)
🍰〆
youtubei のドキュメントが初心者には読みにくくて難しかった……!!
けど、以外と簡単にライブ配信のチャットを取得することってできるんですね~。実は、2年前に一度やったことがあるのですが今回ので色々なライブラリがあることを知れたので、ツールをアップデートしてあげたい!!
過去に作ったツールはコレです。
ちなみに……
チャンネルのIDを取得する検索は、search()
でも可能です。findOne()
はその名の通り1つしか見つけてくれないようですね。
この方法なら複数候補が見つかるケースの場合items
をmap()
とかで良い感じにすることで「どれにする?」みたいな選択ができるかも。あとはチャンネル名で検索とか!
const { Client } = require('youtubei')
const youtube = new Client()
/**
* チャンネルのハンドルから、チャンネルのIDを取得する関数
* @param {string} handleId - チャンネルのハンドル
*/
const run = async handleId => {
const result = await youtube.search(handleId, { type: 'channel' })
console.log(result.items[0].id)
}
// チャンネルのハンドル(https://www.youtube.com/@HANDLE_ID)を記入
const handleId = 'HANDLE_ID'
run(handleId)
あとライブ配信が複数あるケースに対応する場合は、上記に加えてlive.next()
を行う事でライブ配信(予約・配信中・配信済み)を一覧で取得できるのでこっちのアプローチが良さげかも!
const { Client } = require('youtubei')
const youtube = new Client()
/**
* チャンネルのハンドルから、ライブ配信の一覧を取得する関数
* @param {string} handleId - チャンネルのハンドル
*/
const run = async handleId => {
const result = await youtube.search(handleId, { type: 'channel' })
const channel = result.items[0]
await channel.live.next()
console.log(channel.live.items)
}
// チャンネルのハンドル(https://www.youtube.com/@HANDLE_ID)を記入
const handleId = 'HANDLE_ID'
run(handleId)
もしツールを作ることがある場合は、コレも参考にしてみてね✨