Tagwire Plugin で記事内容からタグ候補を自動的に抽出する

Posted by
ぴろり
Posted at
2005/11/15 18:05
Trackbacks
関連記事 (0)
Comments
コメント (16)
Post Comment
コメントできます
Category
プラグイン カテゴリ
 新しく記事を書いていて、以前に使ったタグと同じものを紐付けたいと思った時、 過去にどんなタグを使ったのか迷うことがよくあります。 Tagwire Plugin の作者である小川宏高さんは、 タグを入力する際に JavaScript を使って入力単語の補完を行い、タグ候補をリストアップする方法を提案されています。 しかし、可能であれば記事内容からキーワードを抽出し、これをタグとして自動的に設定してくれると更に楽チンです。

 先日の記事において我々はキーワード付与のための強力な正規表現式を手にしています。 そこで今回、これを利用して記事内容からタグ候補を自動的に抽出できるようにしてみました。 当サイトには 170 弱のタグが登録されている程度('05年11月現在)ですが、 なかなか好調で記事作成時のタグ入力作業が随分と楽になりました。

この記事を Delicious に追加する   このエントリーをはてなブックマークに追加  

はじめに

 文章解析をきっちりと考えるなら、 形態素解析 だとか出現頻度の統計まで考える必要がありますが、 設置の手間が大きかったり、とりわけそこまで抽出精度を要求するわけでもないので今回は使用しません。 実際のところ、タグデータベースがある程度充実してくると、 そこから生成された正規表現式を使うだけで実用上は十分だったりします。 タグがある程度の数が登録されるまでは自前でタイプする必要がありますが、データベースが充実してくるに従い、 不要なものを削除して、特に新しく紐付けたいタグについてだけ入力すれば済むようになってくるはずです。

Tagwire plugin の改造

 Ogawa::Memorandaで公開されている 小川宏高さん作の Tagwire pluginを基本に、 キーワード抽出に必要な正規表現式を得るための改造を済ませておきます。
 そして更に、tagwire.pl に以下のコードを追加します。
;# ++piroli, '05/11/14
MT::Template::Context->add_container_tag('TagsAutoDiscovery' => &tags_auto_discovery);
sub tags_auto_discovery {
    my ($ctx, $args, $cond) = @_;

	;# Tags Auto Discovery does work only when '[?]' is appeared as keyword.
	;# When keywords of the entry is blank or has any words, do nothing.
	my $entry = $ctx->stash ('entry')
			or return $ctx->_no_entry_error ('MT'. $ctx->stash ('tag'));
	$entry->keywords =~ m/^[?]$/
			or return $entry->keywords;

	;# retrieving parameters and set default values
    # count *** NOT YET IMPLEMENTED ****
#    my $count = $args->{count} || 0;

	;# retrieving the regexp pattern of keywords
	my $re;
	if (defined (my $regexp_file = $args->{regexp_file})) {
		;# @see <$MTInclude file="..."$>
		$re = MT::Template::Context::_hdlr_include (
				$ctx, {'file' => $args->{regexp_file}}, $cond);
	} elsif (defined (my $regexp_template = $args->{regexp_template})) {
		;# you can use the file which is built with Index Template
		;# @see <$MTLink template="..."$>
		my $file_path = MT::Template::Context::_hdlr_link (
				$ctx, {'template' => $args->{regexp_template}}, $cond);
		my $site_url = $ctx->stash('blog')->site_url;
		my $site_path = $ctx->stash('blog')->site_path;
		$file_path =~ s/Q$site_urlE/$site_path//;
		$file_path =~ s//+///g;
		$re = do $file_path;
	} elsif (defined (my $regexp_module = $args->{regexp_module})) {
		;# it may be the performance issue when too many tags
		;# but you can control this behaviour by parameters :)
		;# @see <$MTInclude module="..."$>
		$re = MT::Template::Context::_hdlr_include (
				$ctx, {'module' => $args->{regexp_module}}, $cond);
	} else {
		;# it may be the performance issue when too many tags
		;# and be called with using all args as default values :(
		$re = eval tags_as_regexp ($ctx, $args);
	}
	$ctx->error ('MT'. $ctx->stash ('tag'). ': no regexp is specified.') if (! $re);

	;# build contents within container tag
	defined (my $content = $ctx->stash('builder')->build ($ctx, $ctx->stash ('tokens')))
			or return $ctx->error ($ctx->errstr);

	;# convert $content to EUC temporally because regexp is written in EUC
	use Jcode;
	my $original_charset = getcode ($content);
	$content = Jcode::convert ($content, 'euc', $original_charset);

	;# should not keywordnize in these tags
	my %ignore_tags = ();
	map { $ignore_tags{lc $_} = 1; } split /,/, 'pre,textarea';
	defined $args->{ignore_tags}
			and map { $ignore_tags{lc $_} = 1; } split /,/, $args->{ignore_tags};
	defined $args->{apply_tags}
			and map { $ignore_tags{lc $_} = 0; } split /,/, $args->{apply_tags};

	;# Parse the $content for retrieving the text areas that should be given the keywords
	my $keywordnize = 0;
	my %keyword_list = ();

	use HTML::Parser;
	my $html_parser = HTML::Parser->new (
		start_h => [ sub {
				my ($tagname) = @_;
				$ignore_tags{$tagname} && $keywordnize++;
		} => 'tagname'],
		end_h => [ sub {
				my ($tagname) = @_;
				0 < $keywordnize && $ignore_tags{$tagname} && --$keywordnize;
		} => 'tagname'],
		text_h => [ sub {
				my ($text) = @_;
				if ($keywordnize == 0) {
					$text =~ s{($re)}{
							my $kw = $1;
							$keyword_list{$kw}++;
							$kw;
					}egx;
				}
		} => 'text'],
		default_h => [""]);
	$html_parser->parse ($content);
	$html_parser->eof;

	;# Set the discovered keywords as entry's new one.
	my $keywords = join ' ',
			sort { $keyword_list{$b} <=> $keyword_list{$a} } keys %keyword_list;
	$keywords = Jcode::convert ($keywords, $original_charset, 'euc');
	$entry->keywords ($keywords);
	$entry->save;# :-)
	return $entry->keywords;
}

追加されるテンプレートタグ

MTTagsAutoDiscovery
<MTTagsAutoDiscovery> コンテナタグは、 コンテナ内に含まれる文章から regexp_XXX で指定された正規表現式を使ってタグの抽出を行い、 発見された単語を MovableType 管理画面の記事の投稿画面のキーワード欄に設定します。 またコンテナタグは、そこに含まれるコンテクストに関係なく、常にそのエントリのキーワードを返します。 つまり <$MTEntryKeywords$> と同じです。
regexp_file
 指定されたファイルの内容をタグ検索のための正規表現式として使用します。 その動作は <$MTInclude file="..."$> に準じます。
regexp_template
 指定されたインデックステンプレートによって生成されたファイルの内容をタグ検索のための正規表現式として使用します。 その動作は <$MTLink template="..."$> に準じます。
regexp_module
 指定されたテンプレートモジュールの構築結果を、タグ検索のための正規表現式として使用します。 その動作は <$MTInclude module="..."$> に準じます。 構築のたびに正規表現式の生成が行われるため、タグ数が増えた場合にパフォーマンス上の問題となる可能性があります。

regexp_file、regexp_template、regexp_module が何れも指定されていない場合、 内部的にデフォルトパラメータを用いて MTTagsAsRegexp を呼び出します。 そのため、構築のたびに正規表現式の生成が行われるため、 タグ数が増えた場合にパフォーマンス上の問題となる可能性があります。

ignore_tags
 デフォルトで pre,textarea,script 内部に含まれるテキストにはタグリンクを生成しません。 他に追加していしたい HTML タグがある場合、それらのタグをコンマで区切って指定することができます。
apply_tags
 ignore_tags とは逆に、その内部でタグリンクを生成させたい HTML タグをコンマで区切って指定することができます。

これらの指定はデフォルトのignore_tags < ignore_Tags < apply_tags の順に優先されます。

テンプレートタグの修正

 記事内容からタグを抽出するため、個別エントリアーカイブ<MTTagsAutoDiscovery> タグを追加します。 regexp_file、regexp_template、regexp_module の詳細については、 はてなキーワードよろしく記事文中のタグを自動リンクする "正規表現式の生成とテンプレートタグの修正"の節を参照してください。
内容(BODY)と追記(extend)部分からタグ候補の抽出を行う
MTTagsAutoDiscovery コンテナは常にキーワード文字列を返すのでコメントアウトしている
<!-- 
<MTTagsAutoDiscovery regexp_template="Tags Regexp">
<$MTEntryBody$>
<$MTEntryMore$>
</MTTagsAutoDiscovery>
-->

使い方

Step1. 記事作成

 エントリの内容(body)と追記(extend)を書いた後、キーワード欄に[?]とだけ入力して、 一旦記事を「公開」の状態で保存してください。 キーワード欄が空欄または、[?]以外の文字列が入力されている時はキーワード抽出を行ないません。

下書き状態でもキーワード抽出できればいいのですが… その方が便利そうです。

Step2. タグ候補の修正

 キーワード欄に発見されたタグ候補が幾つか列挙されているので、必要なだけ削除/追加して再度、保存してください。 保存し忘れると ? タグが登録されたままになります。
この記事を Delicious に追加する   このエントリーをはてなブックマークに追加  



関連記事/トラックバック

関連記事/トラックバックはまだありません

この記事にトラックバックを送るには?

寄せられたコメント (全 16 件中、最新 5 件まで表示しています)

Posted by
ぴろり ◆OLEEi.VOX.ぴろり ◆OLEEi.VOX.
at
2008/12/08 22:47
ID
2VQX5Gxk
久しぶりに質問されると内容を完全に忘れちゃってますね…
METAタグへの記述は,普通に<MTEntryTags>テンプレートタグを書いて対応しています。確か。
Posted by
マサキマサキ
at
2008/12/08 16:48
ID
yq2V.L02
はじめまして。
MT4.2でエントリーの内容を元にタグを自動で生成するプラグインを探して、
こちらにたどりつきました。こちらで紹介している内容はMT4.2でも問題なく動作しますでしょうか?
また、ここではキーワードとして抽出するようになってますが、メタとして自動生成することも可能でしょうか?
お答えいただけるとうれしいです。
Posted by
footbrainfootbrain
at
2006/11/23 16:09
ID
KetScoGU
3.3になっても、
「Tagwire Pluginで記事内容からタグ候補を自動的に抽出する」
機能を便利に利用していますが、
最近、既存のキーワードに無い変な文字を抽出してくるようになりました。
毎回特定の文字ではないのですが、決まって一文字で、
日常使わないような見た事もないような文字の場合が多いようです。
Jcode.pmは"Jcode.pm,v 2.6 2006/07/02 07:56:06"で
Tagwireのタグは全て標準のタグと競合しないようにリネームしてあります。
他にチェックすべきポイントはありますか?
教えていただけると助かります。宜しくお願いします。
Posted by
ぴろりぴろり
at
2006/10/31 10:08
ID
23dd29Ow
3.3からタグ機能が標準装備になりました。
これと何らかの競合を起こしているように思えますが,
ちょっと調査しきれていません。
あと,タグ候補の自動抽出は同様に可能とも思いますが,
おそらく専用にプラグインなりを作って対処することになりそうです。
チョイチョイ☆って感じでは難しいんじゃないでしょうか?
Posted by
footbrainfootbrain
at
2006/10/30 12:19
ID
V0JZ8QgY
先般はお世話になりました。
あれ以来、便利に利用させていただいておりますが、
MTを3.3にアップデートして以来、
キーワード欄に表示されるタグ候補が「,」(コンマ)から始まり表示されます。
,サッカー,シュート,パス…とこんな感じです。
タグ候補を修正してタグ欄にコピーする際に調整すれば利用上の不都合はないのですが、
気になっていたので報告させていただきました。
また、3.3で装備された機能を用いて、Tagwaireを使用せずにタグ候補を自動的に抽出する方法はありますか?
できたらいいな程度の要望なのですが、お知恵を拝借できれば幸いです。

コメントを投稿する

 
 (必須, 匿名可, 公開, トリップが使えます)
 (必須, 匿名可, 非公開, Gravatar に対応しています)
 (必須)
スパム コメント防止のため「投稿確認」欄に ランダムな数字 CAPTCHAについて を入力してから送信してください。お手数ですがご協力のほど宜しくお願いいたします。