概要

作業リポジトリはこちら

友達と「統計を勉強したい」みたいな話をしてたんだけど、統計を勉強するにしても何か対象となるデータが必要で、何かないかなーと思って思いついたのがニコニコのコミュニティだった。APIはちょこっと調べたけど見つからず、スクレイピングすることに。

方法など

2016年6月1日現在、確認できる最新のコミュニティのIDはco3328808であり、単純に考えて332万件ほど作成されたことがあるようで、この中からランダムにIDを抽選して一つ一つHTTPリクエストを投げてhtmlをDBに格納していく。その後htmlを静的に解析して参加人数やタグを調べ、統計処理を行う。

基本的にnode.jsを使用する。他にまともに使える言語がないから、仕方ないね。データの保存にはMongoDBを使う。最終的にデータをまとめるときにPythonとか使うことになるかもしれないけど、まあ先のことはあまり考えない。

MongoDBはスキーマレスだけど、mongooseでスキーマを管理する。デフォルト値とか扱えたりするのは素直に便利。

スキーマの設計は今のところ、

  • com_id : コミュニティのID。co1234の1234に相当するもの
  • raw_html : コミュニティページ全体のHTML
  • status : ページ取得時のサーバーから返ってきたHTTPステータスコード。現存かつオープンなら200、非公開なら403、削除されたなどで存在しないものは400になる。200番台でのレスポンスでなければ取得されたHTMLは捨てている
  • fetched_at : データの取得日時。時系列データとして扱うつもりはないけど、いつ取得したかが後でわからないと困りそうな気がしたので一応

という感じ。このraw_htmlを解析していろいろ項目を増やして行きたいところ。

今回の成果

スクレイピング自体初めてやったので、アクセス制限など食らっても困るので手始めに、co1からco3000000の範囲の1500件だけ取得した。すぐに分かるデータとして、

db.coms.find({status: 200}).count()

db.coms.find({status: 200, com_id: {$lt: 1000000}}).count()

db.coms.find({status: 200, com_id: {$gt: 1000000, lt: 2000000}}).count()

db.coms.find({status: 200, com_id: {$gt: 2000000}}).count()

こんな感じのクエリで1000000ごとに区切って生存・非公開・存在しないコミュニティを数えてみた。

全体の分布

  • 生存コミュニティ(200) : 237件(15.8%)
  • 非公開コミュニティ(403) : 30件(2%)
  • 存在しないコミュニティ(400) : 1233件(82.2%)

co1〜co1000000 の間

  • 生存 : 46件
  • 非公開 : 8件
  • 存在しない : 446件

co1000000〜co2000000 の間

  • 生存 : 102件
  • 非公開 : 10件
  • 存在しない : 390件

co2000000〜co3000000 の間

  • 生存 : 89件
  • 非公開 : 12件
  • 存在しない : 397件

という感じになった。0〜100万は順当に少ない気がするが、100〜200万と200〜300万で生存コミュ数に差があまりないのが気になった。

一応新着コミュニティ一覧を掘っていくと2016年6月1日の時点では http://com.nicovideo.jp/com_new/?page=19419co1が出現する。各ページには30個表示可能なので19418 * 30 + 5582545件つまり58万件ほど公開コミュニティが存在してるようである。最新のコミュニティのIDはco3329277なので、すなわち582545 / 3329277で、17.5%が公開コミュニティとなっている。

標本率から標本誤差が得られるはずなので、追ってその辺の統計学の知識を入れてちゃんとしたデータとして公開したい。

感想

本番はHTMLを解析してからだろうと思っていたが、この時点ですでに面白い。

さっさとHTMLの解析してコミュニティのいろいろな情報を調べて分類してその統計データを整理しようと思ってたんだけど、実際にデータを取ってみると解析に使えるものはたったの237件しかないと心許ないので、ニコニコに迷惑かけないようにHTMLの解析部分を作りながらサンプルを増やしていきたい。

あと、削除されたりそもそも作成されていないコミュニティが参照されたとき

コミュニティが削除された場合、下記いずれの理由に該当します。
コミュニティオーナーが削除した
コミュニティオーナーがniconicoを退会して30日以上経過した
コミュニティ作成後30日以降も、メンバーがオーナー含め1名のみ

という表示されるように、作成日が直近の30日のものは生存バイアスがかからないことになる。一応それを考慮して300万で切ってて、実際に

db.coms.find({status: 200}, {com_id: 1}).sort({com_id: -1}).limit(1)

こんな感じに今回取得した中での最新の生存コミュを取得して作成日を見てみたら2015年8月でちゃんと問題なかった。ただしその反面、2015年8月移行に作成されたコミュニティを含めてないということなので、その辺はちょっとどうにかしないといけないな、という感じ。