Livetube のコメント周りのシステム仕様まとめ
Livetube のコメントを読み書きするツールを作ろうと思い、コメント周りのシステム仕様を調べてみました。分かったことを出来る限りまとめておきます。
ストリームIDを取得する
Livetubeのコメントを読み書きするためには、まずは配信のストリームIDを取得する必要があります。ストリームIDは全ての配信に1つずつ、固有の値が割り振られています。
http://livetube.cc/hatenaテスト/配信テスト
例えばこちらの配信の場合、ストリームIDは「aaaacwrg3amcm」です。
ではどうやって配信のストリームIDを調べるのかですが、以下の2通りの方法があるようです。
配信ページのHTMLソースから取得する
1つ目は、配信ページのHTMLソースからストリームIDを取得する方法です。例えば上記の配信ページのHTMLソースを見てみると、冒頭がこのようになっています。
<!doctype html><html itemscope="itemscope" itemtype="http://schema.org/WebPage"><head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta itemprop="image" content="/stream/aaaacwrg3amcm.snapshot.jpg"> <meta name="viewport" content="width=964, user-scalable=yes">
3行目に早速ストリームIDが出現しているので、これをHTMLパーサなどで抜き出してしまうのが簡単でしょう。
注:
この<meta itemprop="image" content="/stream/aaaaxxxxxxxxx.snapshot.jpg">
という行は、どの配信にも必ず出現するようです。ただし必ず3行目に出現するとは限らないので、何行目に現れても取得できるような柔軟なプログラムにした方がいいかと思われます。
index.live.jsonから取得する
2つ目はこのindex.live.jsonから取得する方法です。こちらのURLには、現在放送中の配信がJSON形式で並んでいます。minaideタグが付いている配信であってもこの一覧に掲載されます。
URLを開いてみると、JSONデータの中に「"id": “aaaaxxxxxxxxx"」という欄がありますが、こちらがストリームIDです。
注: http://livetube.cc/index.live.xml というURLもあります。こちらはXML形式での提供となります。
サンプルソース
前者の「配信ページのHTMLソースから取得する」のサンプルソースです。エラー処理などはサボってます。
from bs4 import BeautifulSoup import requests import re def get_stream_id(url): # リクエストを生成 r = requests.get(url) # htmlを取得 HTML= r.text # HTMLパーサで解析 soup = BeautifulSoup(html, "html.parser") # <!doctype html><html itemscope="itemscope"... meta = soup.find("meta", itemprop="image") # <meta itemprop="image" content="/stream/aa... meta_conetnt = meta["content"] # /stream/aaaacwrg3amcm.snapshot.jpg stream_id = re.split(u'\.|/', meta_conetnt)[2] # aaaacwrg3amcm return stream_id if __name__ == '__main__': url = "http://livetube.cc/hatenaテスト/配信テスト" stream_id = get_stream_id(url) print("ストリームID:" + stream_id)
実行結果:
$ python get_stream_id.py ストリームID:aaaacwrg3amcm
コメントを取得する
上で取得したストリームIDを使い、コメントを取得します。
APIのURL
- 全てのコメントを取得:
http://livetube.cc/stream/ストリームID.comments- n番目以降のコメントを取得:
http://livetube.cc/stream/ストリームID.comments.番号n
例:
http://livetube.cc/stream/aaaacwrg3amcm.comments.1
→コメント番号2~最新のコメントが返ってくる
こちらのURLにGETリクエストをするだけです。
取得したコメントの構成
取得したコメントはHTML形式になっています。他の形式では取得できません。他の形式として扱いたい場合は、HTMLパーサなどでHTMLを上手く分解する必要があります。
HTMLの細かい構成は以下のとおりです。
<div style="margin-top: 5px;">②<span style="float:right;font-size:60%;font-style:italic;">334F65I</span>
③<img src="/userimage/hatenaTest.png" width="64" height="71" align="left" />
④2 : ⑤<a href="/hatena%E3%83%86%E3%82%B9%E3%83%88"><span style="font-size:1em;color:green; font-weight:bolder;">hatenaテスト</span></a> ⑥<span style="">2/10 16:07:19</span><br />
⑦<span style="font-weight:bold;margin-bottom:0px; padding-bottom:0px;">コメント2</span><br clear="left" />
</div>
</div>
- 背景色
- 放送中に書き込まれたコメントは背景色が白になります。
<div style="background-color: #eee;">
- 放送終了後に書き込まれたコメントは、背景色は設定されず、代わりに下線が引かれます。
<div style="border-bottom:solid 1px #808080;">
- 放送中に書き込まれたコメントは背景色が白になります。
- ID
- タグに「nanasiid」が設定されている場合は、この場所にIDが表示されます。
- 設定されていない場合は空欄になります。
- アイコン画像
- レス番号
- レス番号です。
- 名前
- 緑コテの方はフォントが緑色の太文字になり、リンクも設定されます。
<a href="/hatenaTest"><span style="font-size:1em;color:green; font-weight:bolder;">hatenaTest</span></a>
- 緑コテではないが名前を入力している方は、装飾はされず名前だけが書き出されます。
hatenaTest
- 名無しの場合は空欄になります。
- 緑コテの方はフォントが緑色の太文字になり、リンクも設定されます。
- 時間
- 投稿時間が「M/d H:mm:ss」形式で書き出されます。
- 本文
- 本文です。
BAN情報を取得
コメントを取得する際、同時に BAN 情報も受け取ることができます。BAN情報はコメント取得時のレスポンスヘッダに格納されています。
http://livetube.cc/stream/aaaacwrg3amcm.comments.1
例えばこちらのコメントを取得する際のレスポンスヘッダを見てみると、以下のようになっています。
Transfer-Encoding: chunked Access-Control-Expose-Headers: X-JSON Server: LTWS Access-Control-Allow-Origin: * Content-Encoding: gzip X-JSON: {"n":0,"vi":0,"v":5,"m":0,"b":"0K","r":[],"cc":[2,3],"ei":0,"marks":{"2":[-48],"3":[-48]},"mark_thresh":24} Date: Fri, 10 Feb 2017 10:36:00 GMT Content-Type: text/html; charset=UTF-8
注目するべきは X-JSON の marks と mark_thresh です。
- marks
- マークされているコメント番号とそのスコアです。
注: この marks に格納されているコメント番号は、実際のコメント番号よりも1少ない値になっています。つまり、この例ですと marks の値は2と3になっていますが、実際にコメント欄でマークされている番号は3と4だということです。
- mark_thresh
- BANしきい値です。この値以上のマイナススコアを付けられたコメントはBANされます。
よってこの例では、コメント番号3と4がBANされていることが分かります。
新着コメントをリアルタイムで取得する
「新着コメントが付いた瞬間にコメントを取得する」という機能は、コメントクライアントを作る上ではまず間違いなく必要になると思います。実はこの問題は簡単に解決できて、以下のURLに対し、while文などで間を置かずにアクセスし続けるだけでできます。
http://livetube.cc/stream/ストリームID.comments.現在のコメント数
おさらいになりますが、このURLの意味は、例えば現在コメントが4件付いている配信でしたら、「コメント番号4以降(5~最新)のコメントを送ってくれ」ということになります。
このURLに対し間を置かずにアクセスし続けると、新着がない間は返信は0件ですが、新着が付いた瞬間に返信が1件に切り替わります。これによりリアルタイムで新着コメントを受け取ることができるのです。
そんなことをしたらサーバーに負荷がかかるのでは?と思うかもしれませんが、Livetubeのサーバーにはある工夫が施されているので負荷がかからないようになっています。
Livetubeのサーバーは、返せるコメントが0件の場合、次のコメントが付くか180秒間が経過するまでレスポンスを返さないという仕様になっています。これにより1回の接続がなかなか終わらなくなるので、間を置かずにアクセスし続けたとしても、実際は数分に1回しかアクセスしていないことになるのです。
注: ただしクライアント側でタイムアウトを180秒以内に設定している場合は、当然ですがタイムアウトエラーが発生してしまいます。タイムアウトは無しか180秒以上に設定しておきましょう。
180秒以内に新着コメントが付いた場合、取得したコメントの構成で説明した通り、HTML形式のコメントが返ってきます。180秒経ってもコメントが付かなかった場合は空のレスポンス(X-JSONなどのヘッダー情報は書かれているが、本文は空欄)が返ってきます。
サンプルソース
コメントリーダーの超簡易的なサンプルソースです。BAN情報と新着コメントをリアルタイムで取得し、コンソール上に書き出し続けます。
from bs4 import BeautifulSoup import requests import json def read(stream_id, count): # APIのURL api_url = "http://livetube.cc/stream/" + stream_id + ".comments." + str(count) # リクエストを生成 r = requests.get(api_url, timeout=None) # HTMLを取得。ここで最大180秒待機することになります HTML= r.text # ヘッダーからBAN情報を取得 x_json = json.loads(r.headers["X-JSON"]) marks = json.dumps(x_json["marks"]) mark_thresh = json.dumps(x_json["mark_thresh"]) return html, marks, mark_thresh if __name__ == '__main__': count = 0 # 間を置かずにアクセスし続ける while True: html, marks, mark_thresh = read("aaaacwrg3amcm", count) print("マーク情報:" + marks) print("BANしきい値:" + mark_thresh) print("HTML:" + html) # コメント総数を更新 # ルート直下のdivタグの数がコメント数に等しいことを利用しています soup = BeautifulSoup(html, "html.parser") elems = soup.find_all("div", recursive=False) count += len(elems)
実行結果:
$ python read.py マーク情報:{"2": [-48], "3": [-48]} BANしきい値:24 HTML:<div style="background-color: #eee;"> <div style="margin-top: 5px;"> <img src="/userimage/hatenaTest.png" width="64" height="71" align="left" /> 1 : <a href="/hatena%E3%83%86%E3%82%B9%E3%83%88"><span style="font-size:1em;color:green; font-weight:bolder;">hatenaテスト</span></a> <span style="">2/10 16:06:51</span><br /> <span style="font-weight:bold;margin-bottom:0px; padding-bottom:0px;">コメント1</span><br clear="left" /> </div> </div><div style="border-bottom:solid 1px #808080;"> ...
コメントを書き込む
APIのURL
http://livetube.cc/stream/ストリームID.comments
コメントを取得するときのAPIと同じURLです。GETリクエストの場合はコメント読み込みとなり、POSTリクエストの場合は書き込みになります。
パラメータ
- name
- 名前です。名無しの場合はここを空欄にします。**緑コテを付けたい場合はここで緑コテの名前を入力し、加えてLivetubeにログインしておく必要があります**。詳しくは下記の「Livetubeにログインする」を御覧ください。
- c
- コメントです。ここが空欄ですと書き込みに失敗し、以下のレスポンスが返ってきます。
書き込めません:コメントの文字がないようです。
サンプルソース
指定したストリームIDにコメントを送信するだけの単純なサンプルソースです。
import requests def send(stream_id, name, comment): # APIのURL api_url = "http://livetube.cc/stream/" + stream_id + ".comments" # パラメータ params = {"name": name, "c": comment} # リクエストを生成 r = requests.post(api_url, data=params) if __name__ == '__main__': send("aaaacwrg3amcm", "hatenaテスト", "テスト")
Livetubeにログインする
緑コテでコメントを書き込みたい場合、事前にLivetubeにログインしておく必要があります。
URL
こちらのURLに対し、以下のパラメーターをPOSTリクエストすることでログインできます。
パラメータ
- user
- ユーザー名です。
- password
- パスワードです。
セッションIDを取得
ログインに成功すると、レスポンスヘッダの Set-Cookie にセッションIDがセットされて返ってきます。
Transfer-Encoding: chunked Server: LTWS Content-Encoding: gzip Set-Cookie: u=P7EFTUXXOQ7CXWPDVOBXWV3RVIF7TWDLOMWH6R4QFUVJAQKIE76AZK66HEQ2OQHFMBIK5FFF3G6QRUKG3F2L4J34PBQKCH7UQZ6IN4U5LNZQQLVTNBWQDNDGIVKS5EBQCQSQAYSEKNTWX7WWMBBPCJDCNXQOBPUJSW5CPXAJT3TQLCDZDO***************************; Expires=Tue, 16-Feb-2021 14:49:47 GMT; Path=/ Date: Fri, 17 Feb 2017 14:49:47 GMT Content-Type: text/html; charset=UTF-8
Set-Cookie が空欄の場合はログインに失敗しています。
あとはこの Cookie 情報を、コメント送信時の Cookie にそのままセットするだけで、緑コテとして書き込むことができます。ただし上でも書きましたが、このログイン処理に加えて、コメント送信時の name パラメータにも緑コテの名前をセットする必要があります。どちらか片方でも欠けていたら緑コテで書き込んだことにはなりません。
サンプルソース
ログイン処理後にコメントを送信します。
import requests s = requests.session() def login(user, password): global s # ログインURL login_url = "http://livetube.cc/login" # パラメータ params = {"user": user, "password": password} # リクエストを生成 r = s.post(login_url, data=params) # セッションIDを書き出す print("セッションID:" + s.cookies.get("u")) def send(stream_id, name, comment): global s # APIのURL api_url = "http://livetube.cc/stream/" + stream_id + ".comments" # パラメータ params = {"name": name, "c": comment} # リクエストを生成 r = s.post(api_url, data=params) if __name__ == '__main__': login("hatenaテスト", "**********") send("aaaacwrg3amcm", "hatenaテスト", "テスト")
実行結果:
$ python login_and_send.py セッションID:P7EFTUXXOQ7CXWPDVOBXWV3RVIF7TWDLOMWH6R4QFUVJAQKIE76AZK66HEQ2OQHFMBIK5FFF3G6QRUKG3F2L4J34PBQKCH7UQZ6IN4U5LNZQQLVTNBWQDNDGIVKS5EBQCQSQAYSEKNTWX7WWMBBPCJDCNXQOBPUJSW5CPXAJT3TQLCDZDO***************************