7月
26
2009
3

Twitter ボットの作り方 Perl 編 (目次)

一通りの連載が終了したので、最後になりますが、各エントリに簡単な説明を付けた目次を別エントリとしてあげておきます。

  • Twitter ボットの作り方 Perl 編 (1)
    ボット作成の前提となる基本的な機能の動作確認を行います。この部分がうまくいかない場合、そもそもボットを作成する環境が整っていない事になるので重要です。
    • 前書き
    • 目標
    • 用意するもの
    • 方針
    • 下準備
  • Twitter ボットの作り方 Perl 編 (2)
    単にランダムにつぶやくだけの簡単なボットを作ります。cron に登録することで本当の意味でボットとして活動可能になります。
    • 状態ファイルの準備
    • データの準備
    • 実際のスクリプト
    • cron への登録
  • Twitter ボットの作り方 Perl 編 (3)
    Twitter 検索を組み合わせて、ボット宛のつぶやきを処理する方法を解説します。このテクニックは様々なタイプのボットで応用出来ます。
    • ボット作成の方針
    • 設定ファイルの追加
    • 状態ファイルの準備
    • 実際のスクリプト
    • 動作サンプル
    • 改造のヒント
    • 実は…
  • Twitter ボットの作り方 Perl 編 (4)
    Web 上に公開されている RSS を元につぶやくボットを作ります。このスクリプトのままでも、元になる RSS さえ工夫すれば、それだけで多彩なボットに化けさせる事が出来ます。
    • CPAN ライブラリの追加
    • 設定ファイルの準備
    • 実際のスクリプト
    • その他の注意点
  • Twitter ボットの作り方 Perl 編 (5)
    Web 上で公開されている外部 API を使用した例です。ここでの例は実用性があまり無いですが、ボットの可能性の広がりを感じてもらえればと思います。
    • CPAN ライブラリの追加
    • アプリケーション ID の取得
    • 設定ファイルの準備
    • 実際のスクリプト
    • あと書き
Written by Otchy in: Development | タグ: ,
7月
26
2009
3

Twitter ボットの作り方 Perl 編 (5)

Twitter ボットの作り方解説第 5 弾。ボットに対してのつぶやきを検索して、検索結果を返信するボットを作ってみます。
以下は、第 4 弾までの状態を前提としています。

さて、当初の予定だと Google 検索を行った結果を返すボットを作ろうとしていたのですが、Google 検索 API は公開をやめてしまっているのですね。知りませんでした…。
こういう時は Yahoo の出番という事で、代わりに Yahoo 検索の結果を返すボットを作ります。
Yahoo の様々な公開 API 群は、Google の後追いで公開を始めただけあって優れたものが多いのですが、いかんせん Google の陰に隠れがちですね。

CPAN ライブラリの追加

例によって、Yahoo 検索を便利に実行するためのライブラリが CPAN に登録されているので、インストールしておきます。

$ su -
# perl -MCPAN -e shell
cpan> install Yahoo::Search

アプリケーション ID の取得

Yahoo の検索 API を使うためには、あらかじめアプリケーション ID というものを取得する必要があります。
Yahoo のアカウントを持っていれば、簡単に取得出来るので、以下のサイトからあらかじめ取得しておきます。
Yahoo! デベロッパーネットワーク

大手のウェブサービスで API を公開している場合、「API キー」とか、「デベロッパー ID」とか、呼び方はそれぞれですが、大抵はのこアプリケーション ID のようなものが用意されています。
これらの ID は、各 API が過剰に利用されていないかを監視したり、必要に応じて利用を止めたりする目的で用意されています。
とはいえ、Yahoo 検索のケースで、1日あたり 5000 回とかなので、普通の個人が利用する分には制限についてさほど気にする必要はありません。

設定ファイルの準備

今回作成するボットは、第 3 弾で作った特定ワードに返信するボットと、基本的な作りは一緒です。
設定には前述のアプリケーション ID を追加し、最後のつぶやきの管理には、sinceId を使います。

config.yml

username: "your_twitter_user_name"
password: "your_twitter_password"
appId: "your_yahoo_application_id"

status.yml

sinceId: 0

実際のスクリプト

#!/usr/bin/perl

# 使用するモジュールの読み込み
use strict;
use warnings;
use Encode;
use FindBin;
use YAML::Tiny;
use Net::Twitter;
use Yahoo::Search;

# yml ファイルの読み込みと、Twitter モジュールの初期化
my $config = (YAML::Tiny->read($FindBin::Bin . '/config.yml'))->[0];
my $status = (YAML::Tiny->read($FindBin::Bin . '/status.yml'))->[0];
my $twit = Net::Twitter->new(username => $config->{'username'}, password => $config->{'password'});

# 各種設定値の取得
my $username = $config->{'username'};
my $appId = $config->{'appId'};
my $sinceId = $status->{'sinceId'};

# Twitter 検索。to: オプションでボット宛のつぶやきすべてを取得
my $response = $twit->search({q => 'to:' . $username, since_id => $sinceId});

# Twitter 検索結果を 1 件ずつ処理
for my $result ( reverse( @{$response->{'results'}} ) ) {
    my $query = Encode::encode('utf8', $result->{'text'});
    my $fromUser = $result->{'from_user'};
    my $statusId = $result->{'id'};

    # ボット宛の @ 部分を取り除く
    $query =~ s/\s*\@${username}\s*//;

    # Yahoo 検索 (最大5件、日本語圏で検索)
    my @results = Yahoo::Search->Results(
        Doc => $query,
        AppId => $appId,
        Count => 5,
        Language => 'ja'
    );

    # Yahoo 検索結果を 1 件ずつ処理
    for my $page (@results) {
        # ボットに対するつぶやきへの返信
        my $reply = '@' . $fromUser . ' ' . $page->Title . ' ' . $page->Url;
        $twit->update({status => $reply, in_reply_to_status_id => $statusId});
    }
    $sinceId = $statusId;
}

# 最後に処理したつぶやきの ID を保存
YAML::Tiny::DumpFile($FindBin::Bin . '/status.yml', {sinceId => $sinceId});

ここまで作ってきたボットの内容を理解していれば、ほとんど自明のことしか無いかと思います。
Yahoo 検索についても、Yahoo::Search という優れたライブラリがあるので、非常にシンプルに記述出来ています。

あと書き

「Twitter ボットの作り方 Perl 編」の連載はこれで終わりになります。

連載の終盤で、RSS や外部の API を利用するボットを作成しました。
今のウェブ上ではこういった情報発信が広く行われており、複数のサービスを組み合わせることで多彩なボットを作成可能です。
また、スクレイピング等の手法も組み合わせれば、まさにその組み合わせはアイディア次第。無限の可能性があると言っていいでしょう。
もちろん、一発ネタのボットで注目を集めるというのも面白いそうです。

この連載が、そんな素敵ボットを作ろうとする方の一助となれれば幸いです。

Written by Otchy in: Development | タグ: ,
7月
19
2009
2

Twitter ボットの作り方 Perl 編 (4)

Twitter ボットの作り方解説第 4 弾。特定の RSS の内容をつぶやき続けるボットを作ってみます。
以下は、第 3 弾までの状態を前提としています。

CPAN ライブラリの追加

これまでは、通信する先として Twitter サービスにしか通信していなかったので、Net::Twitter モジュールだけで済んでいましたが、さらに外部の RSS を取得するために CPAN ライブラリを追加します。
例によって各種設定は、特に必要のない限りすべてデフォルト (そのまま Enter) で構いません。
インストールに時間がかかるのでしばし待ちましょう。

$ su -
# perl -MCPAN -e shell
cpan> install XML::FeedPP

Perl 上で RSS を扱うライブラリというと、XML::RSS が有名なのですが、今回は Atom フィードも同等に扱いたかったので XML::FeedPP を採用しました。
RSS と Atom についての関係はこのあたりを参照してみて下さい。
よく分からなければそんなに気にしなくて構いません。
(正確に表現するなら RSS と Atom を合わせてフィードと呼称すべきですが、あえてここでは一般的な名称として RSS という呼称を使用しています。)

設定ファイルの準備

これまでと同じように、config.yml と status.yml を使います。今回はそれぞれ下記のような感じです。

config.yml

username: "your_twitter_user_name"
password: "your_twitter_password"
source: "http://www.otchy.net/feed/rss/"

status.yml

lastHash: ""

source はもちろん接続先 RSS のアドレスですね。
lastHash は最後につぶやいた RSS アイテムのハッシュ値を記録します。
なぜこのようなものが必要になるかというと、RSS や Atom のフォーマットには様々な種類があり、唯一のアイテムを指定する ID のようなものが必ずしも提供されないからです。
従って、複数の値を組み合わせたハッシュ値を用いて、それを元に最後のつぶやきを管理します。

実際のスクリプト

#!/usr/bin/perl

# 必要なライブラリの読み込み
use strict;
use warnings;
use Encode;
use FindBin;
use YAML::Tiny;
use Net::Twitter;
use XML::FeedPP;
use Digest::MD5;

# 各種設定値の取得
my $config = (YAML::Tiny->read($FindBin::Bin . '/config.yml'))->[0];
my $status = (YAML::Tiny->read($FindBin::Bin . '/status.yml'))->[0];
my $twit = Net::Twitter->new(username => $config->{'username'}, password => $config->{'password'});
my $source = $config->{'source'};
my $lastHash = $status->{'lastHash'};

# RSS の読み込み
my $feed = XML::FeedPP->new($source);

# 最後につぶやいた RSS アイテムより新しい RSS アイテムを調べる
my @updates;
for my $item ($feed->get_item()) {
    my $hash = &calcHash($item);
    last if $hash eq $lastHash;
    # 配列の頭から詰めていく事で、つぶやきの順番を RSS と逆にする
    unshift(@updates, $item);
}
# RSS の内容を順につぶやく
for my $item (@updates) {
    my $update = $item->title() . ' ' . $item->link();
    $twit->update($update);
    $lastHash = &calcHash($item);
}

# 最後につぶやいた RSS アイテムのハッシュ値を保存
YAML::Tiny::DumpFile($FindBin::Bin . '/status.yml', {lastHash => $lastHash});

# RSS からハッシュ値を計算する
sub calcHash {
    my $item = shift;
    my $id = $item->guid();
    my $url = $item->link();
    my $title = $item->title();
    my $pubDate = $item->pubDate();
    my $hashKey = '';
    $hashKey .= $id if $id;
    $hashKey .= $url if $url;
    $hashKey .= $title if $title;
    $hashKey .= $pubDate if $pubDate;
    return Digest::MD5->md5_hex($hashKey);
}

第 3 弾までの内容を把握していれば、そんなに目新しい部分は無いかと思います。
ポイントとなるのは、今回 CPAN からインストールして読み込んだ XML::FeedPP と、Digest::MD5 でしょう。

XML::FeedPP は、RSS や Atom の読み書きをするためのライブラリで、RSS の複数のバージョンおよび Atom を透過的に扱う事が出来る優れものです。
大抵のサイトで用意されている RSS (Atom) であれば、ライブラリ任せで上手く読み込む事が出来ます。
RSS のバージョンなどにかかわらず、共通の方法で情報を取得できるのが楽でいいですね。

Digest::MD5 は、Perl 標準のモジュールで、MD5 形式のハッシュ値を計算するためのものです。
ハッシュ値の元になる文字列には、タイトルや URL 等を連結した文字列を使っています。
なので、その文字列自体をそのままキーとして使用してもいいのですが、見栄えが良くないので MD5 化したものをハッシュ値としています。

また、ハッシュ値の計算には、guid、URL、タイトル、更新日時の全てを使っているため、そのいずれかが変更されただけで別の投稿として認識されてしまいます。
そのことの都合が悪い場合、例えば URL だけで同一性を判断するなど、条件を調整してみてください。

その他の注意点

cron の間隔は、RSS が更新される間隔に合わせて適宜調整して下さい。通常は 30 分間隔くらいが適当ではないでしょうか。
なお、配信される RSS ファイルが UTF-8 であることを前提としています。もし異なる文字コードで配信されていたら、つぶやく前に変換をしないといけないですが、UTF-8 以外の RSS ってあまり聞いたことが無いので大丈夫だと思います。

このくらいまで出来るようになると、かなり実用的なボットが作成可能ですね。
自分の欲しい RSS だけをマージして、フィルタリングした結果の RSS を Yahoo! pipes で作ってボットにつぶやかせるとか、応用の幅が広いです。
次の 第 5 弾で、一応本連載は完了します。最後は外部のウェブサービスと連携する例として、Google Yahoo 検索をするボットを作ってみます。

Written by Otchy in: Development | タグ: ,
7月
16
2009
2

自分のサイトの合計はてブ数を淡々と csv に記録し続ける

Twitter ボットの作り方 Perl 編 連載中ですが、ちょっとお休みして、さっき思い立って書いたスクリプトを紹介します。

最近、TopHatenar の更新が止まってしまっていて悲しいので、自分のサイトのトータルはてブ数を記録して、自分でグラフを書くことにしました。
といっても、自分が見る専用で問題ないので、csv を出力しておくだけです。適宜 Excel とかで加工してグラフ化します。

まず必要になる CPAN モジュールをインストールします。root 権限必須です。

$ su -
# cpan
cpan> force install XMLRPC::Lite
cpan> install DateTime

XMLRPC::Lite は自分の環境だとテストで引っかかって上手く入らなかったので、force install してます。
force install する前に、通常の install で入れられないかを試すべきです。
いずれのインストールも結構時間がかかるので、原則放置でたまに Enter を押してやる感じです。

実際のスクリプトは下記のような感じです。
標準出力に吐くようにしたので、ファイルに記録したい場合は任意にリダイレクトしてやります。

#!/usr/bin/perl

use strict;
use warnings;
use XMLRPC::Lite;
use DateTime;

my $url = 'http://www.otchy.net';

my $count = XMLRPC::Lite
    ->proxy('http://b.hatena.ne.jp/xmlrpc')
    ->call('bookmark.getTotalCount', $url)
    ->result;

my $now = DateTime->now(time_zone => 'Asia/Tokyo');
print $now->strftime('%Y-%m-%d') . ',' . $count . "\n";

$url の値はカウント対象の URL ですが、もちろん変更して下さい。
スクリプトに実行権限を与えて実行すると、こんな感じの出力が出てきます。

2009-07-16,204

毎日の記録を残すのであれば、例えば下記のように cron に登録すればいいです。

00 00 * * * /home/your_id/hatebulog/hatebulog.pl >> /home/your_id/hatebulog/hatebulog.csv

今のところ日次で記録する前提で日付しか出力していませんが、はてブ数が多くもっと頻繁に記録するのであれば、出力を下記のように変える事で実行時間も記録されます。

print $now->strftime('%Y-%m-%d %H:%M:%S') . ',' . $count . "\n";

最初に自分専用とはいったものの、いったん csv に落としてしまえば、PHP + Google Chart API でグラフ化して Web から見れるようにするとかもありですね。

追記(2009-08-04)

グラフ化してみました!

Written by Otchy in: Development | タグ: ,
7月
10
2009
5

Twitter ボットの作り方 Perl 編 (3)

Twitter ボットの作り方解説第 3 弾。特定のワードが含まれたつぶやきに反応して、何らかの返信をするボット作ってみます。
以下は、第 2 弾までの状態を前提としています。

ボット作成の方針

基本的なボットの動作としては、まず、特定ワードの検索をして、検索結果に含まれるユーザに対して何らかの返信をするという方法になります。
ただ検索をする際、Twitter 全体を対象として検索すると、ワードによってはあり得ない件数の検索結果となってしまったり、プログラムに間違いがあった場合、不特定多数に迷惑をかけてしまう事になりかねません。
そこで検索対象としては、ボットに @ してくれた人の発言だけを対象とする事にします。
検索対象を全体に広げるのは、ボットの属性で考えたり、プログラムの正常動作を確認出来てからでいいでしょう。

また、ボットの動作は cron による一定の間隔になるので、1 回の動作の間に大量のつぶやきがあった場合、一度に取得できる検索結果件数には限度があるので、検索結果に漏れが出る可能性があります。
この漏れに対しても、厳密に検索を繰り返して遡っていく事は可能ですが、その部分には対応しない事にします。
さしあたってその程度の精度のボットを作ると思って下さい。まあ Twitter という文化からすれば許容出来る範囲でしょう。

あと返信の内容についてですが、何らかの反応があった方が面白いので、基本的にはオウム返しになりますが、「@username えー○○って( 艸`*)ププッ」(○○には相手の発言が入る) という感じにしましょう。

設定ファイルの追加

第 1 弾から使用している config.xml に対して、元々の username と password の後ろに、今回新たに 2 つの値を追加します。

query: "ダメ"
template: "えー「{text}」って( 艸`*)ププッ"

query が検索ワードで、template がそれに対する返信のひな形になります。
投げられた発言を埋め込む場所としてあらかじめ {text} を入れ込んでおいて、ここを置換します。

状態ファイルの準備

返信を送る際に、同じ人に何度も続けて返信をしてしまうと非常に失礼…というかこれはもうボットのバグなので、最後に返信をしたつぶやきを記録しておいて、次に検索する時はそれより後のつぶやきを検索するようにします。
最後に返信したつぶやきの id を次の検索時の検索条件として、since_id というもので指定するので、第 2 弾と同様、status.xml にそれを記録するようにしましょう。

sinceId: 0

初期値は最も小さい id、すなわち 0 を入れておきます。

実際のスクリプト

#!/usr/bin/perl

# 使用するモジュールの読み込み
use strict;
use warnings;
use Encode;
use FindBin;
use YAML::Tiny;
use Net::Twitter;

# yml ファイルの読み込みと、Twitter モジュールの初期化
my $config = (YAML::Tiny->read($FindBin::Bin . '/config.yml'))->[0];
my $status = (YAML::Tiny->read($FindBin::Bin . '/status.yml'))->[0];
my $twit = Net::Twitter->new(username => $config->{'username'}, password => $config->{'password'});

# 各種設定値の取得
my $username = $config->{'username'};
my $query = Encode::encode('utf8', $config->{'query'});
my $template = Encode::encode('utf8', $config->{'template'});
my $sinceId = $status->{'sinceId'};

# Twitter 検索。to: オプションでボット宛のつぶやきだけを絞り込み
my $response = $twit->search({q => 'to:' . $username . ' ' . $query, since_id => $sinceId});

# 検索結果を 1 件ずつ処理
my $result;
foreach $result ( reverse( @{$response->{'results'}} ) ) {
    my $text = Encode::encode('utf8', $result->{'text'});
    my $fromUser = $result->{'from_user'};
    my $statusId = $result->{'id'};
    my $reply = $template;
    $text =~ s/\s*\@${username}\s*//;
    $reply =~ s/{text}/$text/;
    $reply = '@' . "${fromUser} ${reply}";
    $twit->update({status => $reply, in_reply_to_status_id => $statusId});
    $sinceId = $statusId;
}

# 最後に処理したつぶやきの ID を保存
YAML::Tiny::DumpFile($FindBin::Bin . '/status.yml', {sinceId => $sinceId});

さて、ではポイントを絞って解説します。

まず重要なのが Twitter 検索の使い方です。
パラメータ q に検索文字列を指定して検索しますが、ここでは Twitter検索 で使用可能なオプションを指定することが出来ます。
検索時に指定出来るオプションについては、このあたりを参照して下さい。

パラメータ since_id は、つぶやきの id を指定することで、それより後のつぶやきを対象として検索します。
これがないと、重複返信をしてしまうのでとても重要なオプションです。

検索結果を 1 件ずつ処理するループでは、まず検索結果を配列として取得し、その順番を反転します。
検索結果は新しいものから順に返されるのですが、それに対する返信は古い順に行うからです。

検索結果の内容から、text, from_user, id を取り出します。
それぞれ、つぶやきの内容、つぶやいた人、つぶやき ID に相当します。

それ以降の部分は、ひな形の内容とつぶやきの内容を元に、返信する文章を組み立てています。
この部分まで詳しく解説するのは大変なので、詳細については別途 Perl について学んでみて下さい。

また、返信をつぶやく時の記述ですが、これまでになかった in_reply_to_status_id というオプションを指定しています。
このオプションは、どの発言に対する返信なのかを明示するもので、特に必須というわけではないです。
ですがこれをつけることで、返信から元の発言に対してリンクが張られるようになるので、返信の場合はつけておくのがマナーと言えます。

最後に動作確認が完了したら cron に登録しましょう。
あまり頻繁に検索すると API 制限にかかるので、5 分おきくらいにチェックするのが適当ではないでしょうか。

動作サンプル

otchy : @damedashi おれはもうダメだよ…
damedashi : @otchy えー「おれはもうダメだよ…」って( 艸`*)ププッ
otchy : @damedashi そんなダメとか繰り返さないでくれよ
damedashi : @otchy えー「そんなダメとか繰り返さないでくれよ」って( 艸`*)ププッ

※このボットは実在しません

改造のヒント

今回、ボットの返信に使うひな形は文章を 1 つだけにしましたが、第 2 弾で紹介した list.yml を使ったランダムテキストを組み込めば、バラエティーに富んだ返信をする事が可能です。
つぶやきの内容の一部を別の言葉に置換して返信しても面白そうですね。
list.yml の内容を配列ではなく、「キーワード:値」のペアにしておいて、「○○を△△に空目」ボットなんていうのも作れそうです。
また、検索自体のオプションを工夫すれば、もっと色々なボットも考えられるでしょう。

実は…

とここまで書いてきましたが、上記のスクリプトは期待したとおりの動作をしません。
それはなぜかというと、Twitter の検索 API では、日本語の検索結果がリアルタイムには検索出来ず、数時間の遅れがあるためです。
しかしながら、日本語の検索は 7 月末を目処に提供されるという話があるので、それまでは現状でリアルタイム検索が可能な英語表記でテストをしつつ待ちましょう。

さて、だんだんと本格的なボットになってきましたね。
第 4 弾以降では、ボット内だけで完結せず、外部の情報を読み込んで使用するようなボットを作ってみたいと思います。

Written by Otchy in: Development | タグ: ,

Powered by WordPress | Aeros Theme | TheBuckmaker.com