2008/01/31


muumoo.jpPlaggerで取得したGoogleブックマークのフィードを整えるFilter:GoogleBookmarksFeedを書いたけど日本語消えちゃう (管理人日記)という記事より。
喜んだのもつかの間、日本語の文字を含むタグやコメントを書くと、その文字が消えてしまうようです。Plaggerではありがちな問題なような気がしますが、このPluginでも起きてしまいました。
確かに、ブラウザ上からだと日本語は見えるんですが、どうやらGoogleさんはUser-Agentを見て勝手にencodingをISO-8859-1に変えておられるようです。
# curl -L 'https://www.google.com/bookmarks/?output=rss' -u username:password
<?xml version="1.0" encoding="ISO-8859-1"?><rss vers
...
config.yamlの先頭に global:
  timezone: Asia/Tokyo
  user_agent:
    agent: Mozilla/5.0
を入れたら取得出来ました。
ブクマコメントで書こうかと思いましたが、記事が半月程前のものなので管理人さんも見てないかと思い、記事にしました。

それよりも...LivedoorClip.pmで Plagger [info] plugin Plagger::Plugin::Subscription::Config loaded.
Plagger [info] plugin Plagger::Plugin::UserAgent::AuthenRequest loaded.
Plagger [info] plugin Plagger::Plugin::Filter::GoogleBookmarksFeed loaded.
Plagger [info] plugin Plagger::Plugin::Publish::LivedoorClip loaded.
Plagger [info] plugin Plagger::Plugin::Bundle::Defaults loaded.
Plagger [info] plugin Plagger::Plugin::Aggregator::Simple loaded.
Plagger [info] plugin Plagger::Plugin::Summary::Auto loaded.
Plagger [info] plugin Plagger::Plugin::Summary::Simple loaded.
Plagger [info] plugin Plagger::Plugin::Namespace::HatenaFotolife loaded.
Plagger [info] plugin Plagger::Plugin::Namespace::MediaRSS loaded.
Plagger [info] plugin Plagger::Plugin::Namespace::ApplePhotocast loaded.
Plagger::Plugin::Aggregator::Simple [info] Fetch https://www.google.com/bookmarks/?output=rss
Plagger::Plugin::UserAgent::AuthenRequest [info] Adding credential to Google Search History at www.google.com:443
Plagger::Cache [debug] Cache HIT: Aggregator-Simple|https://www.google.com/bookmarks/?output=rss
Plagger::Plugin::Aggregator::Simple [debug] 200: https://www.google.com/bookmarks/?output=rss
Plagger::Plugin::Aggregator::Simple [info] Aggregate https://www.google.com/bookmarks/?output=rss success: 15 entries.
Died at C:/Perl/site/lib/WWW/Mechanize.pm line 1705.
なエラーが出る。なんぞ?
とりあえずcpan upgrade行ってきます。

追記1
GoogleBookmarksFeedで、tagsは1個でも配列で返ってきてそうだったので以下のように修正してます。もしかしたら間違ってるかも *** GoogleBookmarksFeed.pm.orig Tue Sep 04 11:39:49 2007
--- GoogleBookmarksFeed.pm  Tue Sep 04 11:40:15 2007
***************
*** 22,28 ****
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my @orig_tags = @{$args->{orig_entry}->{entry}->{$ns}->{bkmk_label}}) {
              $args->{entry}->tags(@orig_tags);
          }
      }
--- 22,28 ----
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my @orig_tags = $args->{orig_entry}->{entry}->{$ns}->{bkmk_label}) {
              $args->{entry}->tags(@orig_tags);
          }
      }
追記2
大嘘ついてました。tagsは1つの場合は文字、2つ以上の場合は配列で戻るみたいです。 *** GoogleBookmarksFeed.pm.orig Tue Sep 04 11:39:49 2007
--- GoogleBookmarksFeed.pm  Tue Sep 04 14:54:17 2007
***************
*** 22,29 ****
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my @orig_tags = @{$args->{orig_entry}->{entry}->{$ns}->{bkmk_label}}) {
!             $args->{entry}->tags(@orig_tags);
          }
      }
  }
--- 22,33 ----
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my $orig_tags = $args->{orig_entry}->{entry}->{$ns}->{bkmk_label}) {
!           if (ref($orig_tags) eq "ARRAY") {
!               $args->{entry}->tags($orig_tags);
!           } else {
!               $args->{entry}->tags([$orig_tags]);
!           }
          }
      }
  }
Posted at by




昨日書いた「pythonで動作するPlagger「habu」でtwitterにポストするプラグイン書いた」を作者の方がご覧になられ、野良プラグイン置き場のcommit権限を頂きました。
さっそく、昨日の「twitterPost.py」もcommitさせて頂きました。
で、今日はperl版のPublish::Gmailを移植。
おおよそPublish::Gmailを取り込めてますが、viaをサポート出来ていません。
ソースは適当に(いつもながら)こんな感じ...

# -*- coding: utf-8 -*-
__author__ = 'mattn.jp@gmail.com'
__version__ = '0.1'

import smtplib
import urllib
import urlparse
import re
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
from email.Header import Header
from email.Utils import formatdate
from HTMLParser import HTMLParser
import habu.log as log

class _EnclosureParser(HTMLParser):
  def __init__(self):
    HTMLParser.__init__(self)
    self.base = ""
    self.files = []

  def set_base(self, url):
    if url.endswith("/"):
      self.base = url + "index.html"
    else:
      self.base = url

  def handle_starttag(self, tag, attrs):
    if tag.lower() == "img":
      for attr in attrs:
        if attr[0] == "src":
          self.files.append(urlparse.urljoin(self.base, attr[1]))
          break

class MailPublisher(object):
  def __init__(self, config, environ):
    self.mailto = config.get("mailto", "mail to")
    self.mailfrom = config.get("mailfrom", "mail from")
    self.mailroute = config.get("mailroute", "mail route")
    self.encoding = config.get("encoding", "utf-8")

  def execute(self, content):
    try:
      if self.mailroute.has_key("host"):
        s = smtplib.SMTP(self.mailroute["host"])
      else:
        s = smtplib.SMTP()
      for entry in content["entries"]:
        html = """<html>
<body>
""" + entry["description"] + """
</body>
</html>"""
        msg = MIMEMultipart()
        msg['Subject'] = Header(entry["title"], self.encoding)
        msg['From'] = self.mailfrom
        msg['To'] = self.mailto
        msg['Date'] = formatdate()
        msg['X-Mailer'] = "pyhabu::mailPost %s" % __version__
        related = MIMEMultipart('related')
        alt = MIMEMultipart('alternative')
        related.attach(alt)
        ep = _EnclosureParser()
        try:
          ep.set_base(entry["link"])
          ep.feed(html)
        except Exception, e:
          pass
        finally:
          ep.close()
        cnt = 1
        for filename in ep.files:
          fp = urllib.urlopen(filename)
          headers = fp.info()
          if headers.has_key("content-type"):
            mimetype = headers["content-type"].replace("image/", "")
            name = "img%d.%s" % (cnt, mimetype)
            img = MIMEImage(fp.read(), mimetype, name=name)
            img['Content-ID'] = '<%s>' % name
            related.attach(img)
            html = re.compile(r"(<img.*>)").sub(
              lambda x:x.group(1).replace(filename, name), html)
            cnt += 1
        content = MIMEText(html.encode(self.encoding, "replace"), 'html', self.encoding)
        alt.attach(content)
        msg.attach(related)

        s.sendmail(self.mailfrom, self.mailto, msg.as_string())
        log.info("mailPost : commit")
    except Exception, e:
      log.error()
    finally:
      s.close()

def create(config, environ):
  return MailPublisher(config, environ)

※おっ!enclosureもサポートしてんじゃん...
で、用意するYAMLはこんな感じ...
global:
  timezone: Asia/Tokyo
  log: stdout

pipeline:
  rss_fetcher:
    - module: subscription.config
      config:
        feed:
          - http://www.example.com/index.rss
    - module: filter.join
    - module: filter.sort
      config:
        reverse: True
    - module: publisher.mailPost
      config:
        mailto: yourname@gmail.com
        mailfrom: yourname@gmail.com
        mailroute:
          host: mail.example.com
        #encoding: iso-2022-jp

結論:pythonもperlも楽しい。
Posted at by




via twitter/plaggerのポスト

FrontPage - habu Wiki @ SF.jp

habuは、プラグインを組み合わせることでRSSを加工して再配信するためのソフトウェアです。簡単に言っちゃえば,Plaggerもどきです。

plaggerも好きだし、pythonも好きなら試さない訳には行きません。インストールはマイコミジャーナルが詳しいかと思います。私は、いきなりsvn/trunk取得して必要な物はeasy_installしました。

まぁ題名の件で言えば、iTunesで聞いている曲をTwitterにPostするPythonのスクリプト ? TRIVIAL TECHNOLOGIES 2.0を使えば動きそうですが、とりあえず...

habuではpluginはクラスで定義され、実行時にexecuteというメソッドが呼び出されます。気をつけなければならないのがexecuteはperl版と異なりentry単位にhookされるのではなく、全てのcontentsを持って呼び出されます。
※開発中なのかbaseとなるpluginクラスといった物はありません。
またhabuにはまだtemplateが実装されていません。ですのでポスト形式は現状プラグイン本体に記述する事になります。(いずれ改良されるかもしれません)
あと、これはいたし方ないですがsite-packageにあるxxx.pyを使うpublisher/xxx.pyでimport xxx出来ません。
今回のtwitterポストプラグインも「twitterPost.py」という名前にしてあります。
webutilsクラスやlogクラス等で、おおよそperl版と同じような事が出来ます。perlよりもpythonの方が可読性がありますし、pytnonにも負けない程のライブラリ郡もありますから、色んな事が出来るかと思います。Djangoなんかと組み合わせても面白そうです。
あと、面白いなと思ったのがコマンドライン用インタフェース「runhabu.py」に"--download-module"というオプションがあり、svnサーバからダウンロード出来る仕組みがあるようです。こちらは追々試します。
pythonが好きでplaggerも触りたい人は、こちらから初めてみてみるのも良いかもしれませんね。
で、最後に適当に作ったプラグインがコレ
habu/publisher/twitterPost.py
# -*- coding: utf-8 -*-
from twitter import Api
import habu.log as log

class TwitterPublisher(object):
  def __init__(self, config, environ):
    self.username = config.get("username", "twitter username")
    self.password = config.get("password", "twitter password")

  def execute(self, content):
    try:
      api = Api(self.username, self.password)
      for entry in content["entries"]:
        message = entry["title"] + ":"
        if len(entry["summary"]):
          message += entry["summary"]
        else:
          message += entry["description"]
        message += " " + entry["link"]
        if len(message) > 159:
          message = message[0: 159] + "..."
        api.PostUpdate(message)
    except Exception, e:
      log.error()
    else:
      log.info("twitterPost : commit")

def create(config, environ):
  return TwitterPublisher(config, environ)
あと、用意するYAMLはこんな感じ
global:
  timezone: Asia/Tokyo
  log: stdout

pipeline:
  rss_fetcher:
    - module: subscription.config
      config:
        feed:
          - http://b.hatena.ne.jp/[hatena user]/rss
    - module: filter.join
    - module: filter.sort
      config:
        reverse: True
    - module: publisher.twitterPost
      config:
        username: [twitter username]
        password: [twitter password]
deps相当、Crypt相当の物はまだ実装されていませんのでご利用は計画的に。
Posted at by