はてブに参照元付記を始めました

1月から始めてるNews、ニュースサイトとしての記事なんですが、昨日あたりから、部分的ですが参照元を付記するようにしています。

以前もはてなスターとブックマークによるニュース紹介エントリ生成スクリプト - ブログ執筆中にて述べたとおり、このサイトのニュースは、はてなブックマークはてなスターを併用して自動生成しています。では、どうやって参照元を付記しているのかというと、こんな感じ。

id:zoniaフィルタ1つ付ければ類似広告をフィルタリングできれば良いのに

はてなブックマーク - tomityのブックマーク / 2008年2月25日

参照元URIまたはidを<>で囲んで、表記しています。最初は[]で囲むつもりだったのですが、タグとして認識しないことが発覚したので、他のタグと区別するために、このようにしています。

まあ、そんな細かいことはさておき、ニュースサイトが提供できる情報の中で、はてなブックマーク等のSBMでは提供できないものが幾つか提示されていました。ちょっとソース忘れちゃったんですが、

  1. 記事の並び順
  2. 参照元の表記

これくらいですよね。今回、参照元を付記するようになったことで、また一歩ニュースサイトに近づいた訳です。結構違い分からなくなってきたと思うんですが、どうですかね?

でも、参照元の表記くらいでしたら、SBMに組み込めそうなもの。それが出来ないのは、ブラウジングの環境のせいだと俺は思ってます。ネットリテラシがそれなりに高い人が記事を読むとき、RSSリーダーだったり、ニュースサイトであったり、人気ブックマークだったり、お気に入りだったり、様々な参照元から記事を探し、その後、見たい記事のページを開いて、ブックマークを付けます。ただし、あらかじめ複数のページを開いて、後から順々に見ていく人もいれば、一つづつ読んでいく人もいるでしょう。それは各々のスタイルですね。

このように様々なスタイルを維持したまま参照元を付記しようとすると、ページを跨ぐので、リファラーから参照元を推察して取得し、付加するというアプローチをとるしか有りません。経路によっては参照元リファラが取れなくなってしまいますし、あまり現実的なアプローチではないように思います。実現は可能ですが。

今回俺がとったアプローチは、そもそもブラウザ自体をSBMを利用に特化したものにするというものでした。すなわちRSSリーダーとブラウザとはてなブックマークをくっつけてしまうという試みでした。ブログの更新が縮小傾向になることを覚悟しながら作っていた StarRoadがそれです。こういうタイプのブラウザが登場してきても良い時期に差し掛かっているのではないかなと思っています。


ただの宣伝記事に堕ちてしまいましたが、SBMを本当に効率よく使うためには、RSSリーダー、ブラウザ、SBMはセットで提供されるべきだと思うんですよね。ブラウザを見直す時期に差し掛かっているのかもしれません。



なお、参照元の付記に伴い、はてなスターとブックマークによるニュース紹介エントリ生成スクリプト - ブログ執筆中で提供した自動生成スクリプトもこれ専用に改良したので、ここに載せておきます。よかったらどうぞ。

#!/usr/bin/perl -w
use strict;
use warnings;

use Log::Log4perl;

use LWP::RobotUA;
use HTTP::Request::Common qw(GET POST);
use HTTP::Cookies;

use XML::Atom::Feed;
use HTML::TreeBuilder;
use Date::Simple('date','today');


################################
#はてなスター・ブックマークによる、ニュース生成ツール ver1.1
#2008/02/26 presented by tomity
#
#使い方
#./newsGenerator.pl [targetDate] > [output]
#
#targetDate: 収集対象とする日付 today,yesterdayが指定可能
#output: 出力するファイル(これを出力しないと、StdErrと混同してしまう←これバグです。とれない;;)
#
################################



#################################
#コンフィグ
################################
#ロボットの名前
my $agent_name = "blogNewsUpdator";
#メールアドレス
my $from = 'tommy.fmale@gmail.com';
#はてなのユーザー名
my $hatena_name = "user";
#はてなのパスワード
my $hatena_pass = "xxxx";

#はてなブックマークを付けるか、はてなスター$STAR_BOUND以上で掲載の対象とする
my $STAR_BOUND = 2;

#スターのURL
my $starURL = "http://s.hatena.ne.jp/$hatena_name/stars";
#はてなブックマークのフィードのURL
my $hatebu = "http://b.hatena.ne.jp/$hatena_name/atomfeed";
#はてなログインのURL
my $loginURL = "http://www.hatena.ne.jp/login";

#UserAgentの設定
my $ua = LWP::RobotUA->new($agent_name, $from);
my $cookie_jar = HTTP::Cookies->new();
$ua->cookie_jar($cookie_jar);

# 設定ファイルの読み込み
Log::Log4perl::init('/home/konton/perl/blogNewsUpdator/log4perl.conf');

# 設定ファイルに定義したloggerを生成
my $logger = Log::Log4perl::get_logger("mylogger");

###################################################
### はてブの収集
###################################################


my $req = HTTP::Request->new(GET => $hatebu);
my $res = $ua->request($req)->content;


my $feed = XML::Atom::Feed->new(\$res);
$logger->info("load hatena bookmark");


my $target;

my $dayselect = shift;
if($dayselect eq "today"){
  $target = today();
}elsif($dayselect eq "yesterday"){
  $target = today()-1;
}else{
  $target = today()-1;
}


my %linkHash;

$logger->debug("the number of entries :".(scalar $feed->entries));

for my $entry($feed->entries){

  my $url = $entry->link;

  $url = $$url[0] if ref($url) eq "ARRAY";
  $url = $url->href;

  my $title = $entry->title;
  my $comment = $entry->summary;
  my @blockquote;

  next if($comment eq "");
  my $issued = $entry->issued();
  $issued =~ m/^(.*)T/;
  next if(date($1) != $target);

  $logger->debug("bookmark entry"
                 ," url:",$url
                 ," title:",$title
                 ," comment:",$comment);
  my %link =(
    "url" => $url,
    "title" => $title,
    "blockquote" => \@blockquote,
    "comment" => $comment,
  );

  $linkHash{$url} = \%link;
}

###################################################
### はてなスターの収集
###################################################

$logger->info("load hatena star");
# アクセスするURLを登録
my %formdata;
$formdata{'name'} = $hatena_name;
$formdata{'password'} = $hatena_pass;
$formdata{'persistent'} =0 ;

$req = POST($loginURL,[%formdata]);
$req->header(Accept => "*/*");
$req->header("Accept-Language" => "ja");
$req->header("Referer" => $loginURL);
$req->header("Connection" => "Keep-Alive");
$res = $ua->request($req);

die "はてなのログインに失敗しました" unless $res->is_success;
   
$req = new HTTP::Request GET => $starURL;
$req->header(Accept => "*/*");
$req->header("Accept-Language" => "ja");
$req->header("Connection" => "Keep-Alive");
$res = $ua->request($req);

die "はてなスターの取得に失敗しました" unless $res->is_success;
my $star =  $res->content;

my $tree = HTML::TreeBuilder->new();
$tree->parse($star);
$tree->eof();

my @tbodies = $tree->look_down(_tag=>'table',class=>'list');

my @trs = @{$tbodies[0]->content_array_ref };



while(@trs){
  my $tr = shift(@trs);
  next if($tr->tag ne 'tr');
  my @title = $tr->look_down(_tag=>'div',class=>'date');
  next if(scalar(@title) == 0);
 
  my $day = date(${$title[0]->content}[0]);
  last if($target == $day);
}

while(@trs){
  my $tr = shift(@trs);
  my @title = $tr->look_down(_tag=>'div',class=>'date');
  last if(scalar(@title) == 1);
  my $hoge = $tr->look_down(_tag=>'span',class=>'entry-title');
  my $a = $hoge->look_down(_tag=>'a');
  my @bqs = $tr->look_down(_tag=>'blockquote');
  my $url = $a->attr('href');
  my $title = ${$a->content}[0];

  next if(scalar(@bqs) < $STAR_BOUND && !exists($linkHash{$url}));

  my @blockquotes;
  for my $bq (@bqs){ push @blockquotes,${$bq->content()}[0];}
  my $text = "";
  for my $bq (@blockquotes){$text = $text.$bq; }
  $logger->debug("star entry ","url:",$url,",blockquote:",$text);
  
  my %link =(
    "url" => $url,
    "title" => $title,
    "blockquote" => \@blockquotes,
    "comment" => "",
  );
  $logger->debug("star entry"
                 ," url:",$url
                 ," title:",$title);

  if(exists($linkHash{$url})){
    $link{"comment"} = $linkHash{$url}{"comment"};
  }

  $linkHash{$url} = \%link;
}

$tree->delete();


###################################################
### エントリの作成
###################################################

$logger->info("create news entry");
my $day = today()->year."年".today()->month."月".today()->day."日";

my $message = "";
my $subject = "[News] $dayのニュース";

foreach my $linkHashRef(values(%linkHash)){
  my %linkHash = %$linkHashRef;
  $message = $message."***[".$linkHash{'url'}.":title]\n";
  if(scalar @{ $linkHash{'blockquote'}} !=0){
    $message= $message.">>\n";
    $message = $message."<b>$hatena_nameのドキっとした部分</b>\n";
    for my $bq(@{$linkHash{'blockquote'}}){
      $message = $message.$bq."\n";
    }
    $message = $message."<<\n";
  }
  if(!$linkHash{'comment'} eq ''){
    $message = $message.$linkHash{'comment'}."\n";
  }
$message = $message."\n\n";
}

$message = $message.<< "FOOTER";
使用ツール
[http://s.hatena.ne.jp/$hatena_name/:title]
[http://b.hatena.ne.jp/$hatena_name/:title]

FOOTER


print "*".$subject."\n";
print $message;

$logger->info("done.");