Akismetを使ったトラックバック・スパム対策

いい加減にトラックバック・スパムがうざくなってきたので対策してみました。COREBlog2におけるコメント&トラックバック・スパム対策に関しては、清水川さんが纏めてくれているので、それを参考にAkismetというWordPress標準の対策機能を使ってSPAM判定をするようにしました。

1. akismet.py をダウンロードしてパスの通った所(/usr/lib/python2.3など)に置く

2. akismet.pyをZope環境から呼び出せるように設定

[Zopeのディレクトリ]/Products にGlobalModulesという名前のディレクトリを作成
下記の内容の __init__.py というファイルを中に作成
from Products.PythonScripts.Utility import allow_module
allow_module("akismet")

3. WordPressのアカウントを登録して、APIキーを取得

APIキーは、http://wordpress.com/signup/ でサインアップするとメールで送られてきます。

4. ZMIから portal_skins/COREBlog2/tbping を編集して、太字部分のコードを追加
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.utils import log
import akismet

cbtool = getToolByName(context, 'coreblog2_tool')

REQUEST = context.REQUEST
form = REQUEST.form
RESPONSE = context.REQUEST.RESPONSE
entry = context

excerpt = ''
if form.has_key('excerpt'):
    excerpt = form['excerpt']

title = cbtool.convert_charcode(form['title'])
blog_name = cbtool.convert_charcode(form['blog_name'])
excerpt = cbtool.convert_charcode(excerpt)

#Try to add trackback
try:
    #Check SPAM
    is_spam = False
    my_api_key = "xxxxxxxxxxx"
    try:
        real_key = akismet.verify_key(my_api_key, context.absolute_url())
        if real_key:
            is_spam = akismet.comment_check(my_api_key,
                          context.absolute_url(),
                          REQUEST.getClientAddr(),
                          REQUEST.get('HTTP_USER_AGENT', ''),
                          comment_type="trackback",
                          comment_author_url=form['url'],
                          comment_content=form['excerpt'].encode('utf-8'))
    except Exception, e:
        log( 'COREBlog2/tbping: '
                     'akismet exception occured, %s' % e )
    if is_spam:
        raise RuntimeError, 'akismet judged as SPAM. %s' % form['url']
    #Send notify mail if need
    if context.getSend_trackback_notification():
        try:
            to_addr   = context.getNotify_to()
            from_addr = context.getNotify_to()
これで問題のトラックバック・スパムは、ほぼ防げているようです。でも、自分でテストした感じでは、問題のないトラックバックもスパムと判定して弾いちゃうことがあるようです。とりあえず、これでしばらく様子をみますが、”ここはまずいんじゃないの”とか”もっと良い方法があるよ”とかのツッコミは大歓迎。


関連リンク:
Akismet in Python and API docs

Ploneでcontent:encodedを使ってRSS全文配信

今日からRSSフィードで全文配信するように変更しました。Plone(COREBlog2)での設定手順は次の通りです。

Zopeの管理画面から、portal_skins/plone_templatesを開きます。次にrss_templateをクリックし、[Customize]ボタンを押して編集画面に入ります。itemセクションのdescriptionタグを探してください。みつけたら、</description>の後に次の行を挿入します。

<content:encoded
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     tal:content="structure python: obj_item.getBody()">blah</content:encoded>

今まで全文配信してなかったのは、単純にPloneのデフォルトの設定がそうなっていたからです。PloneはCMSなので、Blogエントリだけでなく、かなり長文のコンテンツも管理することができます。そのため、コンテンツの概要(Description)をインデックスとして保持していて、検索結果やRSSにはこの概要を表示するようになっています。

BlogエントリをRSSで全文配信するべきかどうかという議論が少し前に盛り上がっていましたが、Ploneのこの仕様は正しいと思いますね。個人的には、全文読まなくても概要が分かるようなRSSを配信して欲しいです。その内容について詳しく知りたかったら、そのサイトを訪れるので。もちろん、RSSリーダ上で全部読みたいというのも理解できます。上で紹介した方法では、概要に加えてcontent:encodedで記事全文を配信するようにしています。Bloglinesだと、RSSを購読している人の方で概要/全文どちらの表示にするかを選択できるので、これがベストな方法かと。

参考リンク: http://plone.org/documentation/how-to/add-full-body-to-rss-feed

Google Maps API v1からv2への移行

先日、Google Maps API version2が公開されました。変更点は、

  • オーバービュー地図表示の追加
  • 拡張性の高いGMap2クラスの追加
  • JavaScriptライブラリのサイズ軽減
  • メモリーリーク問題の解消
  • デバッグ情報表示機能の追加
  • 詳細な衛星写真を見られる範囲の拡大
  • 1日50万PV制限の撤廃

version1との互換性は、ほぼ保たれているようです。私の Moblog地図 でも、<script>タグの"v=1"を"v=2"にするだけで問題なく動いてしまいました。


これだけでは面白くないので、オーバービュー地図を表示するGOverviewMapControlとGMap2クラスを使ってみることにしました。基本的にGMapをGMap2に、Gpoint()をGLatLng()に置き換えるだけです。注意する点としては、GPoint()とGLatLng()で引数の順序が逆になっていること、Zoomレベルを示す値の大小が逆になっていることです。あとは、メソッドの名前がいろいろと変わっているので、これも注意しましょう( Google Maps API Version 2 Upgrade Guide参照 )。そして、GOverviewMapControlですが、デフォルトだとブラウザ画面の右下に表示されるようになっています。本家のGoogle Mapsだとそれでいいのですが、うちの場合は右側に表示されるMoblog地点のリストと重なってしまいます。なので、次のようにしてオーバービュー地図の位置を地図の右下に移動させました。

function positionOverview() {
    var map = document.getElementById("map");
    var omap = document.getElementById("map_overview");
    x = map.offsetLeft + map.clientWidth - omap.clientWidth;
    y = map.offsetTop + map.clientHeight - omap.clientHeight;
    omap.style.left = x+"px";
    omap.style.top = y+"px";
}

残念なことに、IEでは、このオーバービュー地図が表示されないようです。マーカーをクリックしたときの挙動も何か変だし、API v2はIEとあんまり相性が良くないのかな..

『Ploneによる簡単Webコンテンツ管理』秀和システム

『Ploneによる簡単Webコンテンツ管理 オープンソース徹底活用』という本が発売になっているんですね。著者は、先日のZope Developers Campで一緒だった寺田さんと永井さんです。

既に購入された方によると、

JuNya KOBORI's Miscellaneous Impressions Page
ぱらぱらとめくってみた程度の感想ですが、Zope や CMF などのコアな部分はあまり深く立ち入らない感じで、CMS とは何ぞやから始まって、Plone のインストール、見た目のカスタマイズを行う程度のどちらかというと初心者向けな本になるのでしょーか?? Plone プロダクト紹介に COREBlog2 、 jaMailHost 、 ejSplitter が載ってました。
だそうです。今度、本屋で探してみよう..

CJKSplitterでカタカナ検索

ふと、このサイトのLiveSearch(右上の検索ボックス)でカタカナの検索ができないことに気づきました。

Plone内コンテンツの検索用インデックスを作成するために CJKSplitter という文字分割ツールを使っているのですが、どうもこれに問題があるようです。そういえば、前にlirisさんがCJKSplitterでカタカナのパッチを書いたと言っていました。そこで、lirisさんの日記を探してみるとありました、ありました。

インデックスされたものの中をみると、カタカナのインデックスがないような気がする。漢字やひらがな、英語などは問題なさげ。やっぱり、カタカナはごっそり落としていました。とりあえず、パッチ書こう。 ということで、パッチをacceptしてもらえました。かたかなを知らなかったみたいです。こういう理由は好きです。次のバージョンでカタカナも大丈夫になるはず。  cjksplitter — Emerge Technology

なるほど、CJKSplitterの作者は中国の人でしたから、片仮名を知らないというのも納得がいきます。でも、このパッチは半年たった今でも最新版に反映されていないんですね。でも、その肝心のパッチがみつからない… 仕方がないので、CJKSplitter.pyを見てみると、平仮名の範囲に片仮名の文字集合を追加してあげるだけでした(下記参照)。この後、インデックスを再構築することで無事にカタカナでも検索できるようになりました。

\u3040-\u309f → \u3040-\u30ff

ちなみに私も形態素解析よりもN-gram方式のほうが好きです。形態素解析だと辞書に依存するので、辞書に登録されていない単語は非常に弱いです。N-gramだと、文章をただの文字の並びとしかみないので、検索の漏れも少ないようです。

今回の教訓:
Use the Source, Luke!

Feed meterにランクイン

RSSフィードの「人気度」と「更新頻度」を表示してくれる Feed meter 。これの上位300にランクインしていたようです。集計方法は非公開だそうですが、どういうアルゴリズムになっているんでしょうね。Web型のRSSリーダは、購読者数をUSER_AGENTで教えてくれるんですが、これって外からも取得できるのかな。

Feed meterと同じ方が運営されてる track feed 。他のサイトからリンクが張られたことをRSSでお知らせしてくれるものなのですが、こちらも便利に利用させてもらっています。

Plone 2.1.2 + COREBlog2 に移行

やっと移行しました。平日、帰宅してからの深夜1~2時間くらいを使って、ほぼ1週間かかりました。右上の検索がLiverSeachに対応したり、Googleなどで検索してこのサイトに辿り着いた場合に、入力したキーワードがハイライト表示されるようになったりと、いろいろ変わっています。それから、Plubrickのスキンが使えなくなったので、デザインも大幅に変えてみました。さっぱりした感じで気に入っているんですけど、少し色気が足りない気はします。"エロカッコいい"が目標なんだけど…

Plone 2.0 → 2.1への移行作業ですが、問題が発生してちょっと大変でした。COREBlog2は移行用のスクリプトが用意されています。基本的にmigration/readme-ja.txtに書いてある手順通りに行えば良いはずなんですが、エクスポート本文中に「 charset = Windows-31J 」という文字列が含まれていると次のようなエラーになってしまいました。

エラーの種類
UnicodeError
エラー値
MS932 encoding error:invalid character x8b

この問題を特定するのにかなり時間がかかりました。最初は、不正な文字が混ざっているんだと思って、それらしき文字を探したのですが、全然見つかりません。仕方がないので、旧サイトからエクスポートした2万行のテキストファイルを前半/後半の半分に分けて、migrationスクリプトを実行。エラーが発生したら、その部分を更に2分割して実行というのを繰り返してやっとこの問題をみつけました。とほほな手動2分探索(>_<)

Ploneのコンテンツに同様の「 charset= 」が含まれていてもエラーになるので、COREBlogの問題ではないようです。メーリングリストでも質問してみたものの、同様の症状が出ている人はいないみたいで、あきらめモード。とりあえず、=の前後に空白があればエラーにならないので、それで逃げました。

CMS/Blog Exchange 2

CMS/Blog Exchangeという名の Zope Developers Camp キックオフ飲み会に参加してきました。

参加者は10人弱だったのですが、そこにはZope/Plone界のギーク達(私は除いて)が集まり、中身の濃い時間を堪能できました。早くPlone 2.1+COREBlog2な環境に移行しなきゃと思っていたところに、PloneのMigrationの話も聞けたりして勉強になりました。やっぱり、こういう会に参加するとモチベーションが上がってきますね。

で、2006年冬のDevCampですが、ちょうどその時期に引越しの予定があって、参加するかどうか迷っていたのですが、いつのまにか参加する気満々になっていました。以下のテーマについて、プログラムリーダのもとで勉強したり、成果物を公開できるように作業したりするそうです。基本的に昼はゲレンデに滑りに行って、夕方から夜にかけてハックということになるんでしょうか。

  • 10分で作るPlone + COREBlog2のBlogポータル - 柴田淳さん
  • Plone Default Skinを壊そう - nyusukeさん
  • Plone で使えるプロダクトの日本語化 - 鈴木たかのりさん
  • "最小Product"を拡張しよう - 清水川貴之さん
  • Zope3 Example大量生産スプリント - 田原悠西さん

海外のコミュニティがSkypeで参加するという話もあるとかで、今から楽しみです。

時間を間違えた

19:30〜だそうです。
|ω・)誰もイナイ・・・

今確認したら、今朝ちゃんとリマインダ・メールが入っていました。がっくし。

CMS/Blog Exchange 2会場に到着

というわけで、これから飲み会です。