Perl Hacker な人たちと IRC でチャットしていて、Monday Module とかやらないかっていう話が出ました。要は Photo Friday のパクリで、一週間に一回ネタ出ししてそれについてのエントリを書く、それの Perl スクリプト版。CPAN から適当なモジュールをピックアップして、それをお題として、何かスクリプトを書いてみたらどうだろうという。
面白いモジュールを見つけるのは誰がやるのとか、どうやってエントリを集約するのとか、過去ログはどういう扱いになるのとか、どこに設置するのとかまだ真面目に考えてはないのですが、コンセプトとしては結構面白いかなと思います。
そこでなんとなく予行演習してみます。名付けて "Monday Module ひとり" (劇団ひとりみたいだ) 。なんで Monday なんだというのは特に理由はなくて、語呂が良い (MM と略せるし)とか、月曜にこの話が出たとかそんな感じだったと思います。
ということで、
This week's challenge: "HTML::LinkExtor".
HTML::LinkExtor は HTML 文書の中からリンク文字列を抜き出して返してくれるモジュールです。
これと ping.bloggers.jp の changes.xml を組み合わせて、最近更新のあったウェブログからリンクを抜き出して数えて、多くリンクが貼られているページを抽出するというスクリプトを書きました。至極単純なスクリプトなので、実行結果はノイズだらけなのですが。
#!/usr/local/bin/perl
# changes.xml を元にリンクを数える
#
# Naoya Ito <naoya@naoya.dyndns.org>
# 2003.11.20 作成
use strict;
use warnings;
use HTML::LinkExtor;
use WebService::ChangesXml;
use LWP::Simple;
our %LINKS;
use constant URL => 1;
my $changes = WebService::ChangesXml->new('http://ping.bloggers.jp/changes.xml');
my $pings = $changes->find_new_pings(10800);
for my $ping (@$pings) {
my $base = $ping->{url};
my $content = LWP::Simple::get($base) or next;
my $parser = HTML::LinkExtor->new;
$parser->parse($content);
my %seen = ();
for my $link ($parser->links) {
if (shift @{$link} eq 'a') {
# 相対パスは要らない
next if ($link->[URL] !~ /^https?/);
# 絶対URLでもサイト内リンクは要らない
next if ($link->[URL] =~ /$base/);
# 同じサイトで同一リンクは要らない
next if (exists $seen{ $link->[URL] });
$LINKS{ $link->[URL] }++;
$seen{ $link->[URL] }++;
}
}
}
for my $url (keys %LINKS) {
printf ("%d %s?n", $LINKS{$url}, $url) if ($LINKS{$url} > 3);
}
changes.xml のパースは自分で書いても良かったのですが、先日宮川さんが CPAN に上げた WebService::ChangesXml を利用しました。XML::Simple で XML のパーシングとそのデータ構造を抽象化したクラスです。便利です。
たくさんのサイトに HTTP リクエストを投げるので、本来は fork() するとかスレッドを使うとか、あるいは POE あたりを使うのがいいのですが、例によって面倒なのでそれはせずに、単純にシングル処理。
$ perl bloglinks.pl | sort -nr | more
とかして実行してやると、
84 http://www.movabletype.org 24 http://www.myblog.jp/ 18 http://ping.bloggers.jp/ 12 http://japan.cnet.com/ 10 http://www.movabletype.org/ 9 http://yeah.myblog.jp/ 9 http://xtc.bz/ 9 http://www.zdnet.co.jp/ 9 http://www.watch.impress.co.jp/ 9 http://www.apple.co.jp/ 9 http://slashdot.jp/ 9 http://readmej.com/ 9 http://bulkfeeds.net/ 8 http://www.hotwired.co.jp/ 8 http://www.blogrolling.com/ 8 http://whatisthematrix.warnerbros.com/ 8 http://kotonoha.main.jp/2003/11/09yeah2003all.html 8 http://itpro.nikkeibp.co.jp/ 8 http://coolsummer.typepad.com/kotori/ 7 http://yami.to/ 7 http://x51.org/ 7 http://www5.big.or.jp/%7Ehellcat/news/0311/17pic.html 7 http://www2.diary.ne.jp/user/69964/ 7 http://www14.big.or.jp/~onmars/ 7 http://www.zakzak.co.jp/geino/n-2003_11/g2003111914.html 7 http://www.zakzak.co.jp/geino/n-2003_11/g2003111812.html 7 http://www.zakzak.co.jp/geino/n-2003_11/g2003111413.html 7 http://www.zakzak.co.jp/ 7 http://www.wired.com/ 7 http://www.watch.impress.co.jp/av/docs/20031117/cci.htm 7 http://www.vrnetcom.co.jp/weekly/index.html ...
こーんな感じの結果が。予想通りというか movabletype.org が飛び抜けてる。Powered by Movable Type のところのリンクに引っかかるんでしょうね。せかいのまんなか (止まっちゃってるなあ)とか blogmap みたくするには、精度を上げるためにノイズを除去するとか、ピンポイントで記事への URL だけを抽出するとか、似たような URL や同じページを指す事なる表現の URL とかをどうマージするかとか、サイトのリンクをいくつか辿って巡回(要はロボット)するとか色々考える必要があります。あくまで Monday Module なので、軽いノリで作ったという代物です。 :D
# ノイズの除去は二つのアーカイブを拾って diff すればいいんじゃないかと宮川さんがちらっと言ってまいました。