国立情報学研究所にあるニコニコデータセットをダウンロードする。JSON形式で圧縮されている約50GBの800万動画分コメントデータである。
圧縮されたファイルは解凍しないでそのまま処理することで、HDDが300GBなくても大丈夫なようにした。
ニコニコ大百科女性声優一覧から、いわゆる萌えアニメに出演している声優を選んで288人のリスト cvlist.txt を作る。
タイトルやタグなどの動画情報ファイルである meta.txt と、cvlist.txt に載っている声優がタグに出現する動画のコメントを抜き出したファイル nm...txt ができる。112958の動画が抽出された。
JSONの扱いがなぜかPython 2 ではうまくいかなかったので version 3 でやった。
### Python3 で!!! import json import re import os import tarfile import gzip import unicodedata import MeCab from multiprocessing import Pool ### parallel computing def processor(files): wd = "/niconico/" # データのあるディレクトリ outwd = "/niconico/output/" # 処理したデータを出力するディレクトリ voice = [v0.strip() for v0 in open("cvlist.txt", "rU")] labels = ["video_id", "upload_time", "title", "length", "view_counter", "comment_counter", "mylist_counter"] comment = ["date", "no", "vpos", "comment"] tmpf = files w0 = open(outwd + "meta/" + str(tmpf) + ".txt", "w") # 動画データはひとまとめにする w0.write("\t".join(labels + ["tag"])+"\n") f0 = gzip.open(wd + "meta/" + str(tmpf) + ".dat.gz", "rb") tmpg = f0.read().decode("utf-8").split("\n") p0 = tmpg.pop(-1) # 最後は \n のため "" になる map0 = list(map(lambda x: json.loads(x, "utf-8"), tmpg)) tar0 = tarfile.open(wd + "comment/" + str(tmpf) + ".tar.gz", "r") # コメントデータは動画IDごとに出力する dat = tar0.getnames() for tmp in map0: tags = [t0["tag"] for t0 in tmp["tags"]] # tag を分解 tags = list(map(lambda x: unicodedata.normalize('NFKC', x), tags)) # 半角文字に変換 if any(map(lambda x: x in voice, tags)): # tag が声優をひとつでも含んでいるか a0 = list(map(lambda x: str(tmp[x]), labels)) # 動画情報 if str(tmpf)+"/"+tmp["video_id"]+".dat" in dat: # 削除か何かですべての動画がダンプされているわけではなさそう w0.write("\t".join(a0 + [",".join(tags)]) + "\n") tmpcom = list(tar0.extractfile(tar0.getmember(str(tmpf) + "/" + tmp["video_id"] + ".dat"))) tmpc = list(map(lambda y: json.loads(y, "utf-8"), list(map(lambda x: x.decode().rstrip(), tmpcom)))) a1 = list(map(lambda y: list(map(lambda x: str(y[x]), comment)), tmpc)) for i in range(len(a1)): # 半角文字に変換 a1[i][comment.index("comment")] = unicodedata.normalize('NFKC', a1[i][comment.index("comment")]) w1 = open(outwd + "comment/" + tmp["video_id"] + ".txt", "w") w1.write("\t".join(comment)+"\n") for a2 in a1: w1.write("\t".join(a2) + "\n") w1.close() # コメントの終わり w0.close() # 動画データの終わり p = Pool(4) p.map(processor, files)
MeCab による品詞分解の実行
MeCabのデフォルト辞書にはNAIST、拡張としてはてなキーワードを追加し、声優やアニメなどの固有名詞にも対応できるようにした。しかしはてなキーワードは2010年で更新が止まっているので、これ以降に追加されたものについてはドンマイ。追加の仕方についてはこちら。
# mecab python2 from multiprocessing import Pool import os mecabf = os.listdir(wd) # /niconico/output/comment/ など抽出した動画のディレクトリにある全動画 def processor(files): wd = "/niconico/output/comment/" outwd = "/niconico/output/mecab/" import unicodedata import codecs import MeCab tagger = MeCab.Tagger("-Ochasen") lex = ["名詞", "動詞", "形容詞", "副詞"] f0 = open(wd + str(files), "rU") header = f0.readline().strip().split("\t") w0 = open(outwd + str(files), "w") w0.write("\t".join(header) + "\n") for j in f0: tmp = j.rstrip().split("\t") if len(tmp) == len(header): mec0 = tagger.parse(tmp[header.index("comment")].replace('"', "")).split("\n") # mecab実行。" はEOFエラーとか言われてRで死ぬ gomi = [mec0.pop(-1) for i in range(2)] mec1 = map(lambda x: x.split("\t"), mec0) line = [] for i in mec1: if any(map(lambda x: x in i[3], lex)): # 品詞のどれかがあれば line += [i[2]] # 品詞の基本形を取得する tmp[header.index("comment")] = " ".join(line) w0.write("\t".join(tmp) + "\n") w0.close() p = Pool(4) p.map(processor, mecabf)