理系学生日記

おまえはいつまで学生気分なのか

自分のはてなブックマークからタグとブックマークURLを引っ張る

ホントははてなブックマークから任意文字列を検索して、ヒットしたURLなりなんなりを返すようなperlスクリプト作りたかったんですけど、アイディアも設計も経験もないのに作り始めたので、クソなのができました。
やっぱしプログラムは考えてから作ろう。。。
恥ずかしさで胸いっぱいになっていろいろ勉強することにする!

大失敗

  • アイディアとして、ブックマークURLを得た後、そのURLから本文だけ抜き出してgrepみたいなことをするのを考えていた
  • ブックマークURLぶっこぬくスクリプト書こー
  • よく考えると、全部そんなことしてたらクソ遅くね?
  • ローカルにコンテンツ置いとけばいいんじゃね?
  • もう一回よく考えると、それオンラインのブックマークの利点完全に消してね?
  • 投了しました

設計くらいちゃんとしろという例。

気づいたダメな点

  • コードがダメという以前に
  • WWW::Mechanizeを内部でもつ意味があったのか
  • ブックマークURLを得るためにはタグを予め取得する必要がある
  • タグをdecodeしてまたencodeするとかすることになる。
  • 最初から壮大なスクリプトを空想していたらダメ。それ妄想ですから。
  • おおまかな設計くらい考えてから作る

続々追記予定

言っておくこと

  • WWW::Hatena::Scraperを一部参考にしましたありがとうございました。
    • WWW::Hatena::Scraperの$cansslまわり正しい?

やってること

  • b.hatena.ne.jp/kiririmodeからclassがtag-*になっているリンクをぶっこ抜き(タグのため)
  • b.hatena.ne.jp/kiririmode/$tagからclassがbookmarkになっているリンクをぶっこ抜き(これがブックマークURL)

使い方

use strict;
use BookmarkFinder;

my $bf = BookmarkFinder->new( 'はてなID', 'はてなパスワード' );
my $tags = $bf->fetch_tags;
print join "\n", @$tags, "\n"; # タグ一覧
my $urls  = $bf->fetch_urls( $tags );
print join "\n", @$urls, "\n"; # ブックマークURI一覧

TODO

  • やっぱりデザインパターンとかの勉強する。
  • 日本語の扱いがよくわかってないのでこっちも。
package BookmarkFinder;

use strict;
use base qw(Class::Accessor::Fast);
use vars qw($can_ssl);
use HTML::Feature qw(feature);
use WWW::Mechanize;
use Carp;
use CGI::Lite;

__PACKAGE__->mk_accessors( qw(agent uri_login) );

sub p { print Dumper shift }

BEGIN {
    eval { use Crypt::SSLeay; };
    $can_ssl = ! $@;
}

use constant LOGIN_URL	  => 'http://www.hatena.ne.jp/login';
use constant BOOKMARK_URL => 'http://b.hatena.ne.jp';

# Constructor
sub new {
    my $self = shift;
    my ($id, $pass) = @_;
    $self = bless {}, $self;

    # substitute http with https
    my $login = LOGIN_URL;
    $login =~ s/^http/https/ if $can_ssl;
    $self->{uri_login} = $login;

    $self->{conf}->{name} = $id;
    $self->{conf}->{pass} = $pass;
    $self->{conf}->{dir}  = '.bookmark';

    $self->agent( WWW::Mechanize->new( autocheck => 1 ) );
    $self->agent->agent_alias( 'Windows Mozilla' );
    $self;
}

# login hatena service
sub login {
    my $self = shift;

    my ($id, $pass) = @_;
    $self->agent->get( $self->uri_login );
    $self->agent->submit_form(
	with_fields => { name     => $self->{conf}->{name},
			 password => $self->{conf}->{pass} } );
    $self->agent->success() or croak "login failed";
}

sub fetch_tags {
    my ($self, $id) = @_;
    $id ||= $self->{conf}->{name};

    my $url = BOOKMARK_URL . "/$id";
    $self->agent->get( $url );
    my @tags = map { $_->url() } 
	$self->agent->find_all_links( class_regex => qr/tag-.*/ );
    @tags = map { url_decode( $_ ) }
	        map { m[^/.*/(.*)/$]; $1 } @tags;
    $self->{tags} = \@tags;
    return wantarray? @tags : \@tags;
}


sub fetch_urls {
    my ($self, $tags, $id) = @_;

    $tags ||= $self->{tags};
    $id   ||= $self->{conf}->{name};

    my @urls;
    for my $tag (@$tags) {
	print "processing $tag\n";
	$self->agent->get( BOOKMARK_URL . "/$id/" . url_encode( $tag ));
	push @urls, $self->agent->find_all_links( class_regex => qr/^bookmark$/ );
    }

    my %seen;
    @urls = grep { ! $seen{$_}++ } map { $_->url() } @urls;
    $self->{urls} = \@urls;
    return wantarray? @urls : \@urls;
}


1;

あーfetch_urlsの部分消してしまったぁぁあああ!!(バージョン管理もしてなかた)
あとで書き直す。

ローカルに落とす

オンラインブックマークの利点を最大限消すステキ仕様。
HTML::Featureつかったけど、抽出精度がとてもいい!ステキだ。
ローカルに落としたあとはコマンドラインからgrepする!(アイディアがダメだと気づいた時点で力尽きた)
文字コードはたぶんutf-8?

sub store_contents {
    my ($self, $urls) = @_;
    my $dir = $self->{conf}->{dir};

    for my $url ( @$urls ) {
	my $filename = $url;
	$filename =~ s!^https?://!!;
	$filename =~ s/[\W]/-/g;

	my $filename = "$dir/$filename.dat";
	next if -e $filename;
	
	my %feature = feature( $url );
	print "writing to $filename\n";

	open FILE, '>', $filename or carp "cannot open $filename";
	print FILE $feature{text};
	close FILE or carp "cannot close $filename";
    }
}