はてなブックマークの一日分をまとめて公開する - Perlとの格闘 - その2


ふと気づくと、前の記事がブックマークされてました。記事アップしてから10日以上経ってから(笑)興味ある人いるなら、ってことで続きを書いてみようと思います。


ただはてなブックマークの日付ごとのページからブックマーク情報を吸い出すだけなスクリプトを作成したんですが、なんせperl初心者ですから、ものすごい力技で強引に行ってます。きっともっとよい方法はいくらでもあるんでしょうが、それはおいおい調べていくということで。

作りたいperlスクリプトの仕様


やりたいことは

ということです。


はてなブックマークでは、自分のブックマークが日付ごとに整理されて見ることができるようになっています。

たとえば、私のブックマーク「http://b.hatena.ne.jp/hejihogu/」だと、このURLの後に「20050615」(2005年6月15日)をつけて「http://b.hatena.ne.jp/hejihogu/20050615」とすると、その日のブックマークを見ることができます。

このページのソースファイルを読み込んで、ブックマーク情報の部分だけを抽出すれば、目的が達成できると考えました。


実際のスクリプト内容


実際のスクリプトは以下のとおり。

1:?#! /usr/bin/perl
2:# HatenaBookmark extract tool
3:
4:
5:print "Please input bookmark \"days\" or \"tags\" !\n";
6:print "(Ex. 20051025 or sbm)\n";
7:
8:$day = <STDIN>;
9:$day =~ s/\n//;
10:
11:$dayfile = "$day.txt";
12:$dayfiles = $day . "b.txt";
13:
14:print "$day\n";
15:print "$dayfile\n";
16:print "$dayfiles\n";
17:
18:use LWP::Simple;
19:getstore ("http://b.hatena.ne.jp/hejihogu/$day",$dayfile);
20:
21:open (IN,$dayfile);
22:open (OUT,">$dayfiles");
23:
24: print OUT "<b>【ネット】</b>\n";
25: print OUT "<b>【セキュリティ】</b>\n";
26: print OUT "<b>【はてな】</b>\n\n";
27: print OUT "<b>【PC・デジモノ】</b>\n";
28: print OUT "<b>【ゲーム】</b>\n";
29: print OUT "<b>【漫画・アニメ】</b>\n";
30: print OUT "<b>【デジカメ】</b>\n";
31: print OUT "<b>【音楽・音楽配信】</b>\n";
32: print OUT "<b>【携帯】</b>\n";
33: print OUT "<b>【Mac】</b>\n\n";
34: print OUT "<b>【ネタ・トリビア】</b>\n";
35: print OUT "<b>【食】</b>\n";
36: print OUT "<b>【本】</b>\n";
37: print OUT "<b>【社会】</b>\n";
38: print OUT "<b>【生活】</b>\n";
39: print OUT "<b>【政治】</b>\n";
40: print OUT "<b>【事故・災害】</b>\n";
41: print OUT "<b>【テレビ】</b>\n";
42: print OUT "<b>【サイエンス・研究】</b>\n";
43: print OUT "<b>【その他】</b>\n\n";
44: print OUT "\n\n\n";
45:
46:
47:while(<IN>){
48: @flds = split(/\n/,$_);
49: $sl = join("",@flds); # 各行をまとめる
50: 
51: if(($sl =~ /dt class="bookmark"/) and ($userc == 0)){ # ブックマーク抽出
52: $ahref = $sl;
53: $ahref =~ s/.*mark"><a href/<a href/;
54: $ahref =~ s/a><.*/a>/;
55: $ahref =~ s/blank">/blank">■/;
56: print OUT "$ahref"; # 各bookmarkを含む行の出力
57: ++$count; # ブックマーク数カウント
58: $userc = 1; # ユーザー数チェックモードへの移行
59:
60: }elsif(($sl =~ /class="users"/) and ($userc == 1)){ # ユーザー数処理
61: $auser = $sl;
62: $auserurl = $sl;
63: $auserurl =~ s/.*a href=\"(\S+)/\1/;
64: $auserurl =~ s/\".*//; 
65: $aurl = "<a href=\"http://b.hatena.ne.jp$auserurl"; # リンク先抽出処理
66: $auser =~ s/.*>(\S+) user/\1 aaaaa/;
67:   if($auser == $sl){ # 間違いuserだったらスキップ
68:  }else{
69:
70:  $auser =~ s/ aaaaa.*//; # user以降削除
71:   if($auser > 1){
72:   print OUT ' (',$aurl,'"><b>',$auser,'users</b></a> checked)'; # 複数
73:   }else{
74:   print OUT ' (',$aurl,'"><b>',$auser,'user</b></a> checked)'; # 単数
75:   }
76:  $userc = 2; # コメントチェックモードへの移行
77:  }
78: }elsif(($sl =~ /class="comment"/) and ($userc == 2)){ # コメントかチェック
79:   if($sl =~ /span class="comment"/){ # コメントがあるかチェック
80:   $comment = $sl;
81:   $comment =~ s/.*span class=\"comment\">/\n/;
82:   $comment =~ s/<\/span>.*//;
83:   print OUT "$comment";
84:   }
85:
86:  print OUT "\n\n";
87:  $userc = 0; # コメント処理終了 次のブックマークへ
88: }
89:}
90:
91:print OUT "\n\n\n今日のブックマーク数は";
92:print OUT $count,"でした。\n"; # トータルブックマーク数の表示
93:
94:close (IN);
95:close (OUT);

以下、簡単な説明。

5:print "Please input bookmark \"days\" or \"tags\" !\n";
6:print "(Ex. 20051025 or sbm)\n";
7:
8:$day = <STDIN>;
9:$day =~ s/\n//;
10:
11:$dayfile = "$day.txt";
12:$dayfiles = $day . "b.txt";
13:
14:print "$day\n";
15:print "$dayfile\n";
16:print "$dayfiles\n";

5-16行:得たいブックマークを行った日の入力。このスクリプトで日付だけでなくタグページも同様に抽出できるので、タグもつかえる形にしています。(っていうか、タグも同じURL方式だったので、そのまま使えただけとも言う)

\nは改行。

8行目で$dayに調べたい日(例として20051025、2005年10月25日)を入力してもらい、9行目で改行を削除して、それから$dayfile (=20051025.txt)と$dayfiles (=20051025b.txt)を作成。

この$dayfileは入手するソースファイルの保存先、$dayfilesはそれから抽出したブックマーク情報の保存先となります。

14-16行では一応確認のため、$day, $dayfile, $dayfilesを画面に出力しています。

18:use LWP::Simple;
19:getstore ("http://b.hatena.ne.jp/hejihogu/$day",$dayfile);
20:
21:open (IN,$dayfile);
22:open (OUT,">$dayfiles");

18-22行:LWP::Simpleモジュールを使って、必要なソースファイルの取得。
19行目でソースファイルをゲットして、$dayfileのファイル名で保存しています。
21行目で$dayfile(20051025.txt)からINへ読み込み、22行目でOUTで$dayfiles(20051025b.txt)への出力を指定しています。


25-44行:後で自分でブックマーク情報を整理するための見出しを先に出力しています。

47:while(<IN>){
48: @flds = split(/\n/,$_);
49: $sl = join("",@flds); # 各行をまとめる

47-49行:47行目で$dayfile(20051025.txt)からの読み込み開始。
48行目で読み込んだ数行の塊を\n、つまり改行ごとに区切って@fldsに配列として読み込み、それをまとめて$slとする。以下、この$slを解析する。

51: if(($sl =~ /dt class="bookmark"/) and ($userc == 0)){ # ブックマーク抽出
52: $ahref = $sl;
53: $ahref =~ s/.*mark"><a href/<a href/;
54: $ahref =~ s/a><.*/a>/;
55: $ahref =~ s/blank">/blank">■/;
56: print OUT "$ahref"; # 各bookmarkを含む行の出力
57: ++$count; # ブックマーク数カウント
58: $userc = 1; # ユーザー数チェックモードへの移行

51-58行:ブックマーク情報の抽出部分。
51行で「dt class="bookmark"」を含み、「$userc = 0」なときに、ブックマーク抽出処理を行う判定。「dt class="bookmark"」が含まれるブロックにはブックマーク情報が含まれる。また、$usercというのは、1.ブックマーク抽出 2.ユーザー数抽出 3.コメント抽出という一つのブックマーク情報に対しての処理のどの段階にいるかを表す指標としてつかっています。$userc+1が今の段階です。

52-55行では、置換処理「S///」を用いて、必要部分のみを$ahrefに抽出しています。そのときにブックマークタイトルの頭に「■」を付加しています(55行)。

56行で抽出したブックマーク情報を出力、57行でブックマーク数のカウント、58行でユーザー数チェックモードへ移行することを表すため、$usrecを1に設定します。

60: }elsif(($sl =~ /class="users"/) and ($userc == 1)){ # ユーザー数処理
61: $auser = $sl;
62: $auserurl = $sl;
63: $auserurl =~ s/.*a href=\"(\S+)/\1/;
64: $auserurl =~ s/\".*//; 
65: $aurl = "<a href=\"http://b.hatena.ne.jp$auserurl"; # リンク先抽出処理
66: $auser =~ s/.*>(\S+) user/\1 aaaaa/;
67:   if($auser == $sl){ # 間違いuserだったらスキップ
68:  }else{
69:
70:  $auser =~ s/ aaaaa.*//; # user以降削除
71:   if($auser > 1){
72:   print OUT ' (',$aurl,'"><b>',$auser,'users</b></a> checked)'; # 複数
73:   }else{
74:   print OUT ' (',$aurl,'"><b>',$auser,'user</b></a> checked)'; # 単数
75:   }
76:  $userc = 2; # コメントチェックモードへの移行
77:  }

60-77行:ユーザー数の抽出部分。
60行で「class="users"」を含み、「$userc = 1」なときに、ユーザー数抽出処理を行う判定。
61-66行で$auser(そのブックマークのユーザー数)、$auserurl(ブックマークエントリへのリンクURLの一部)、$aurl(ブックマークエントリへのリンクURLを含むhtml書式)を決定。
70-74行でユーザー数で単数形、複数形を選んで出力。
76行でコメントチェックモードへの移行。

78: }elsif(($sl =~ /class="comment"/) and ($userc == 2)){ # コメントかチェック
79:   if($sl =~ /span class="comment"/){ # コメントがあるかチェック
80:   $comment = $sl;
81:   $comment =~ s/.*span class=\"comment\">/\n/;
82:   $comment =~ s/<\/span>.*//;
83:   print OUT "$comment";
84:   }

78-84行:もしコメントをつけていた場合に、コメントを抽出する。
78行で「class="comment"」を含み、「$userc = 2」なときに、コメント抽出処理を行う判定。
79行でコメントがついているかの判定。
ついていたら、80-82行で$commenにコメントを抽出し、83行で出力。

86:  print OUT "\n\n";
87:  $userc = 0; # コメント処理終了 次のブックマークへ
88: }
89:}
90:
91:print OUT "\n\n\n今日のブックマーク数は";
92:print OUT $count,"でした。\n"; # トータルブックマーク数の表示
93:
94:close (IN);
95:close (OUT);

86-95行:ループ、抽出作業終了処理。
86-87行でひとつのブックマーク抽出処理を終了し、&usercを0へとリセットして次のブックマーク抽出処理へと。
91-92行では、ループ終了後にその日のトータルブックマーク数($count)を出力。
最後に94-95行でopenしたのをcloseで閉じて終了。


たぶんPerlに詳しい人がみたら突っ込みどころ満載なソースでしょうが、自作一つ目のスクリプトってことで指摘していただけたら幸いです。


ソースファイルからブックマーク情報を判定、抽出の部分は、実際にソースファイルを表示して、判定条件を考えながら作成しました。ソースファイルの仕様が変更になると、改良しなくちゃだめそうです。


なんかこんなことをしなくてもブックマーク情報を楽に取得できるしくみがあったりするんでしょうか。Perlの勉強になったからこれはこれで良かったんですが、こんなベタな方法じゃなくてもっとスマートなやり方がありそうな気がするなぁ。


このPerlスクリプトで出力されたファイルにコメントをつけて、へじの雑記帳で公開してるんですが、各見出しへの分類までブックマークにつけたタグ情報を元にうまく自動分類できないかなぁと考えています。でも、非常に面倒になりそうなので保留中。(自分のつけたタグと見出しの関連を設定しなきゃいけないので)



このスクリプトは、WindowsXP上のActivePerl環境ではてなにログインした状態で動作しますが、もし誰かこれを動かそうと思った人は

19:getstore ("http://b.hatena.ne.jp/hejihogu/$day",$dayfile);

の「hejihogu」の部分を自分のユーザー名に変更する必要があります。逆に言えば、この部分を変えれば、どのユーザーのブックマークも取得、抽出することができます。