« Nグラム文字分割による単語頻度分析 | トップページ | コメントデータベースから特定著者の発言抽出 »

ブログのコメントのデータベース化

今回は、ブログのコメントをデータベース化する方法について書いてみます。
データベース化と言ってもデータベースシステムにデータを入れるのではなく、幾つかのキーを使った抽出や集計が行いやすいようにタブ区切りテキスト化するだけです。

題材としては、おなじみの「せと弘幸Blog『日本よ何処へ』」を使わせていただきます。

おそらく、コメントを記述するHTMLの構造はLivedoorのブログに共通していると思うので、Livedoorブログであれば他のブログにも応用可能だと思います。

タブ区切りテキストの形としては以下を想定します。(実際のコメントから内容例を引用しています)

filename	blogdate	comauthor	comdate	comtime	post	body
29704240.html 2005年08月05日 瀬戸弘幸 2005-08-05 20:22 Posted by 瀬戸弘幸 at 2005年08月05日 20:22 ある意味、日本は情ない。 戦略的思考がゼロです。
50282975.html 2005年11月28日 瀬戸弘幸 2005-12-01 17:01 Posted by 瀬戸弘幸 at 2005年12月01日 17:01 たしかにおっしゃられる点は理解します。ハンチントンにおかされているか、そうでないかは別にしても、不気味な世界情勢になっていることは確かではないでしょうか。

これらのカラムは以下の意味を持ちます。

filename: そのコメントが存在するブログのファイル名
blogdate: そのブログの記事が書かれた日付
comauthor: コメント著者(後ろのpostから抽出)
comdate: コメント日付(後ろのpostから抽出)
comtime: コメント時刻(後ろのpostから抽出)
post: コメント投稿情報(HTMLの<div class="comments-post">タグの内容)
body: コメント本文(HTMLの<div class="comments-body">タグの内容)

こういう形にしておくと、コメント著者名やコメント本文を区別した検索が簡単になります。
例えば、コメント著者名として「瀬戸弘幸」氏が現れるコメントを検索したいのであれば、
^.*\t.*\t瀬戸弘幸\t
という正規表現を使えばできます。ここまで正確にしなくても「\t瀬戸弘幸\t」でも十分でしょう。(ちなみに、瀬戸氏は「せと弘幸」や瀬の異字体を使った名前でもコメントされているので、もっと単純な「弘幸\t」がかなり正確で網羅的な検索条件になるかと思います)

また、例えば、「新風連ヲチスレテンプレ置き場」にある以下の指摘に出てくるコメントを検索するには「弘幸\t.*\t.*\t.*\t.*弘幸氏.*$」とすれば良いです。コメント著者名が「弘幸」で終わっており、かつ、コメント本文中に「弘幸氏」という文字が入っているコメントを検索しています。

(2)2008年3月、再び自演失敗。
ttp://s04.megalodon.jp/2008-0325-2253-05/blog.livedoor.jp/the_radical_right/archives/51862209.html
>ただし大阪の大衆は割合ざっくばらんです。まずは行動する集団を支持するのです。
>維新政党新風副代表瀬戸弘幸氏は、確かに「大阪というギルド」に風穴をあけました。
>
>Posted by せと弘幸 at 2008年03月25日 22:21

このデータベース化をするPerlプログラムは以下です。もちろん、何の動作保証もありませんので、試したい方は自己責任でお願いします。

use strict;
use utf8;
use HTML::TreeBuilder;
use Encode;

# 出力ファイル名(コメントDB)指定
my $cdbfile = "D:\\temp2\\commentdb.txt";

# コマンドの第一引数にディレクトリのパスが与えられていると仮定
my $dirname = shift;
my @filelist;
# 指定されたディレクトリをOpenし、99999999.htmlのファイル名のみを@filelistに入れる
# @filelistの中にあるファイルだけが処理対象
opendir(DIR, $dirname) || die("error: cannot open $dirname\n");
while (my $file = readdir(DIR)) { 
  if( $file =~ /^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\.html$/ ) {
    push (@filelist, $file); 
    }
  }
closedir(DIR);
if( @filelist == 0 ) { die("error: filelist empty\n"); }

# ディレクトリパスの最後が\でなければ補っておく
if( $dirname !~ /\\$/ ) { $dirname = $dirname . "\\" ; }

open(CDBOUT, ">:utf8", $cdbfile); # UTF8指定を忘れずに
print CDBOUT "filename\tblogdate\tcomauthor\tcomdate\tcomtime\tpost\tbody\n";  # 見出し行

foreach my $file ( @filelist ) {
  print "$file\n";  # 進行状況が見えるように、ファイル名をコンソールに出力
  my $tree = HTML::TreeBuilder->new;
  my $filepath = $dirname . $file ;
  # Livedoor BlogはEUCなのでEUC指定しているつもりなのだが正しくないかも。
  #open(HTML, "<:euc_jp","$filename") || die("cannot open $filename\n");
  open(HTML,"<:encoding(euc-jp)","$filepath") || die("cannot open $filepath\n");

  my @line = ;
  close(HTML);
  foreach my $line1 (@line) {
    $tree->parse($line1);
  }
  $tree->eof();

  my $title = ($tree->find("title"))->as_text;    # titleタグの中身を抽出
  my $date = ($tree->find("h2"))->as_text;     # 最初のH2タグの中身は日付と思う

  # コメント本体とポスト日付・主名の抽出
  my @cbody = $tree->look_down("class","comments-body");
  my @cpost = $tree->look_down("class","comments-post");
  
  if( @cbody != @cpost ) {  # 両者の個数が違う場合は対応が崩れていると判断しスキップ
    print "skip!! number of body and post is not equal: $file\n";
    next;
  }
  if (@cbody == 0) { next; }  # コメント数が0なら次へ
  
  my $i;
  for( $i=0; $i < $#cbody ; $i++ ) {
    print CDBOUT "$file\t";    # 1カラム目:ファイル名
    print CDBOUT "$date\t";  # 2カラム目:日付(ブログ記事自体の)
    my $post = $cpost[$i]->as_text; # Post情報から改行とタブを削除
    $post =~ s/\n//g;
    $post =~ s/\t//g;
    my $body = $cbody[$i]->as_text; # Body情報から改行とタブを削除
    $body =~ s/\n//g;
    $body =~ s/\t//g;
    if( $post =~ "^Posted by *(.*) at ([0-9][0-9][0-9][0-9]).+([0-9][0-9]).+([0-9][0-9]).+([0-9][0-9]:[0-9][0-9])\$" ) {
      print CDBOUT "$1\t", "$2-$3-$4\t", "$5\t", "$post\t", "$body\n";
      # $1: 投稿者名、$2-$3-$4: yyyy-mm-dd、$5: hh:mm となっている筈
    } 
    else { die "posted by pattern mis match: $post\n"; }
  }
  $tree->delete; # Deleteは必須。こうしないとメモリが開放されない。
}
close CDBOUT;
print "finished\n";

このプログラムはコマンドの第一引数にgetHTMLで取得したarchivesディレクトリのパスが指定されると想定しています。例えば、このPerlプログラムのファイルがblog2cdb.plだとすると、以下のようなコマンドラインで起動します。
blog2cdb.pl "D:\temp2\getHTML\blog.livedoor.jp\the_radical_right\archives"
ちなみに、私のロートルPCでは2008年10月22日分までの記事で実行に1時間以上かかりました。
(getHTMLの話は、「テキスト処理の道具達(1):Webページの収集ツール」に載せました)

生成するファイルは以下のようにPerlの中に直に書いてますので、変更したい時はPerlプログラムを修正してください。
my $cdbfile = "D:\\temp2\\commentdb.txt";

コメントの認識は、HTML中に以下のような構造で記述されている事を利用しています。つまり、コメント本文は<div class="comments-body">というタグの中に現れ、著者情報は<div class="comments-post">というタグの中に現れるという構文です。

        <div class="comments-body">
たしかにおっしゃられる点は理解します。ハンチントンにおかされているか、そうでないかは別にしても、不気味な世界情勢になっていることは確かではないでしょうか。</div>
        <div class="comments-post">Posted by 瀬戸弘幸
at 2005年12月01日 17:01</div>

但し、この構造には例外があります。本文中でコメントを引用する際に、このタグが一緒にコピーされている例があります。この場合、現在のプログラムの作りでは、本文の中でのコメント引用なのか、コメント本体なのかを区別できません。また、本文中のコメント引用は、記事を書く人が勝手にできるのか、<div class="comments-body">と<div class="comments-post">が対をなさない構造も書けてしまうようです。こういう勝手な構造を作られると正常に認識できなくなるので、せめてもの対処として、両者の数が同じであるかをチェックしています。数が異なるHTMLがあった場合には、HTMLファイルの手修正で対応するしかありません。

|

« Nグラム文字分割による単語頻度分析 | トップページ | コメントデータベースから特定著者の発言抽出 »

文字分析」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1109195/26719483

この記事へのトラックバック一覧です: ブログのコメントのデータベース化:

« Nグラム文字分割による単語頻度分析 | トップページ | コメントデータベースから特定著者の発言抽出 »