たこ録

ちょこちょこと書きつけている勉強録です

mutt でフィードの購読管理

(前回までの取り組みで得られた)購読フィードの更新差分データを(python スクリプトによって)メールボックス(今回は Maildir 形式)に流し込み、メールクライアント(今回は mutt)を用いて未読/既読/保存などの購読管理を行えるように取り組んでみます。

更新差分の取得(前回までの取り組み)

フィードの更新差分の取得は、前回までに取り組んだ以下の3つのスクリプト

(初回実行) read_opml.py
               |  opmlファイルから購読フィードの情報を読み取る
               |  csv(?)形式(サイト名,URL,更新間隔(分))で書き出し
               |
(準備/メンテ時) manage_pkl.py
               |  (上記)書き出したcsvファイルの読み込み
               |  pickle モジュールで直列化
               |
(通常実行) check_feed.py
               フィードの更新差分を取得する

を、だいたい次のような流れで

###### 設定ファイルの準備 ######
google-reader-subscriptions.xml (opml形式)
           |
     (read_opml.py の実行)
           |
     config-group.csv           (サイト名,URL,更新間隔(分) (csv形式))
           |
     (manage_pkl.py の実行)
           |
     config-group.pkl      ([[title,url,interval,last_executed,latest],...])
                           (フィード毎に5つの要素、pickleモジュールで保存)

###### 実行 ######
     config-group.pkl
           |
     (check_feed.py の実行)
           |
     [エラーなし] config-group.pkl(新), config-group.pkl-bak(旧)
     [エラーあり] config-group.pkl-err(新), config-group.pkl(旧)
     [更新あり] config-group.pkl-feed-時刻 <--- (今回利用する)更新差分データ
           |
     ([エラーあり] の場合 manage_pkl.py)
           |
     (check_feed.py の実行)
           |

利用することによって取得します。なおこの更新差分データは、(cPickle モジュールを用いて) 次の形式

[
  ('サイト名', {エントリのデータ}),
  ('サイト名', {エントリのデータ}),
  ...
]

で保存しています。各スクリプトの中身や取り組みの顛末など詳しくは当ブログの過去エントリ

をご覧ください。

スクリプトの作成

上記のスクリプトの実行により作られる更新差分データファイルを読み込んで、指定するメールボックスへ更新情報をおさめるスクリプトを作ろうと思いますが、だいたい次のかたまり

def check_enc(data,ccode):
  文字列(data)を指定文字コード(ccode)にエンコードする
  return エンコード後の文字列

def gen_tz():
  for サイト名,エントリデータ in 更新差分データを読み取る:
    エントリ投稿時刻('date' or 'pubDate')を読み取る
    タイムゾーン(JST)にあわせて補正する
    yield (サイト名, エントリデータ, 補正後の投稿時刻)

def make_idx():
  更新情報からメールメッセージ(*)を作成する
    (* 1エントリにつき
          タイトル,リンク,投稿時刻,サイト名 の情報を、
       (更新分すべて)1通のメッセージにまとめる)
  指定メールボックスに作成したメールを追加する

def make_item():
  更新情報からメールメッセージ(*)を作成する
    (* 1エントリにつき
          タイトル,リンク,エントリ本文,投稿時刻,サイト名 の情報を、
       1エントリに1通のメッセージにおさめる)
  指定メールボックスに作成したメールを追加する

を考えつつ、(文字コードと時刻の扱いにはまりながらorz)あれこれ練っていって...

# coding: utf-8

from email.mime.text import MIMEText
from time import strftime, strptime, mktime, localtime
import mailbox
import cPickle

def check_cc(data,ccode):
  def gen(s,cc):
    u = unicode(s)
    while u:
      try:
        u.encode(cc)
      except UnicodeEncodeError, err:
        yield u[:err.start] + '?'
        u = u[err.end:]
      else:
        yield u
        u = ''
  return reduce(lambda x,y: x+y, gen(data,ccode))

def gen_tz(pkl):
  for t,i in cPickle.load(pkl):
    toJST = lambda tz: \
      (tz in ['-05:00','EST'] and 14) or \
      (tz in ['+00:00','+0000','GMT','UTC'] and 9) or 0
    if i.has_key('pubDate'):
      pdtz = ['EST', 'GMT', 'UTC', '+0000', 'JST']
      tstr,tz = ((i['pubDate'][-3:] in pdtz) and (i['pubDate'][:-3], i['pubDate'][-3:])) or \
                ((i['pubDate'][-5:] in pdtz) and (i['pubDate'][:-5], i['pubDate'][-5:]))
      tdata = strptime(tstr, '%a, %d %b %Y %H:%M:%S ')
    else:
      tstr,tz = i['date'][:-6],i['date'][-6:]
      tdata = strptime(tstr, '%Y-%m-%dT%H:%M:%S')
    published = mktime(tdata) + toJST(tz)*60*60
    yield (t, i, published)

def make_idx(pkl_path, mdir_path, ccode):
  def gen(pkl, cc):
    for idx,item in enumerate(sorted(gen_tz(pkl),key=lambda x: x[2],reverse=True)):
      t,i,p = item
      link = (i.has_key('origLink') and i['origLink']) or i['link']
      content = '%d: %s\n%s\n[%s] (%s)\n\n' % \
          (idx+1, check_cc(i['title'],cc), link,
              strftime('%a, %d %b %Y %H:%M:%S %Z',localtime(p)), check_cc(t,cc))
      yield content.encode(cc)

  msg = MIMEText(reduce(lambda x,y: x+y, gen(open(pkl_path,'rb'),ccode)),
      'plain', ccode)
  msg['From'],msg['To'],msg['Subject'] = pkl_path,'takoloku',pkl_path

  mb = mailbox.Maildir(mdir_path)
  mb.add(mailbox.MaildirMessage(msg))

def make_item(pkl_path, mdir_path, ccode):
  def gen(pkl, cc):
    for t,i,p in gen_tz(pkl):
      link = (i.has_key('origLink') and i['origLink']) or i['link']
      summary = (i.has_key('encoded') and i['encoded']) or i['description']
      subj = check_cc(i['title'],cc)
      body = '<p>%s(%s)</p>\n<p>%s</p>\n<div>%s</div>\n' % \
          (check_cc(i['title'],cc), check_cc(t,cc), link, check_cc(summary,cc))
      msg = MIMEText(body.encode(cc), 'html', cc)
      msg['Date'],msg['From'],msg['To'],msg['Subject'] = \
          strftime('%a, %d %b %Y %H:%M:%S %Z', localtime(p)), t, 'takoloku', subj.encode(cc)
      yield msg

  mb = mailbox.Maildir(mdir_path)
  for msg in gen(open(pkl_path,'rb'),ccode):
    mb.add(mailbox.MaildirMessage(msg))

...

"""
今回も和訳ドキュメント

Python ライブラリリファレンス
  > 7. インターネット上のデータの操作
    > 7.1 email -- 電子メールと MIME 処理のためのパッケージ
http://www.python.jp/doc/release/lib/module-email.html
    > 7.3 mailbox -- 様々な形式のメールボックス操作
http://www.python.jp/doc/release/lib/module-mailbox.html
  > 13. データの永続化
    > 13.1 pickle -- Python オブジェクトの整列化
http://www.python.jp/doc/release/lib/module-pickle.html
  > 14. 汎用オペレーティングシステムサービス
    > 14.2 time -- 時刻データへのアクセスと変換
http://www.python.jp/doc/release/lib/module-time.html

などを参考にさせていただきました。なお、手元の環
境はdebian(lenny) のパッケージを利用させてもらっ
ています。 (ありがとうございます m(_ _)m)
"""

とします。

動作の確認

上記のスクリプトを次のファイル構成

$ ls -F1
check_feed.py
config-test.csv
config-test.pkl
config-test.pkl-bak
config-test.pkl-feed-20507419
data/
feed_to_mailbox.py
manage_pkl.py

において確認するべく、次の記述

###### feed_to_mailbox.py ######
...

if __name__ == '__main__':
  data_path = 'config-test.pkl-feed-20507419'
  base_path = '******(mailbox path)******'
  mdir_path = [''.join([base_path,path]) for path in ['feed-A','feed-B']]
  char_code = 'ISO-2022-jp'

  make_idx(data_path, mdir_path[0], char_code)
  make_item(data_path, mdir_path[1], char_code)

を追加して実行してみると...

$ python feed_to_mailbox.py
$ mutt
...

...(まず make_idx() の確認)...

make_idx() の確認 1

...(開いてみると)...

make_idx() の確認 2

となります。ここでエントリのタイトル名にふっている以下の番号

From: config-test.pkl-feed-20507419                                             
Subject: config-test.pkl-feed-20507419                                          
To: takoloku                                                                    
                                                                                
1: スクエニ、『ブラッド オブ バハムート』のPV先行公開と壁紙を期間限定配布       
http://journal.mycom.co.jp/news/2008/12/23/005/index.html                       
[Tue, 23 Dec 2008 23:30:39 JST] (マイコミジャーナル)                            
                                                                                
2: ポニーキャニオン、乙女向けCDで新展開 - コミケ75で先行販売も                  
http://journal.mycom.co.jp/news/2008/12/23/004/index.html                       
[Tue, 23 Dec 2008 23:04:03 JST] (マイコミジャーナル)                            
                                                                                
3: 【レポート】デザインの現場では「QuarkXPress 8」をどう評価しているのか?       
http://journal.mycom.co.jp/articles/2008/12/23/quarknow/index.html              
[Tue, 23 Dec 2008 18:52:00 JST] (マイコミジャーナル)

...

(1, 2, 3, ...)は、mutt と urlview を併用した際の番号

make_idx() の確認 3

UrlView 0.9: (120 matches) Press Q or Ctrl-C to Quit!

->    1 http://journal.mycom.co.jp/news/2008/12/23/005/index.html
      2 http://journal.mycom.co.jp/news/2008/12/23/004/index.html
      3 http://journal.mycom.co.jp/articles/2008/12/23/quarknow/index.html
      ...

とで、指定ブラウザでエントリを開く際の目安にと考えています。

...(次に make_item() の確認)...

make_item() の確認 1

...(開いてみると)...

make_item() の確認 2

となります。こちらは urlview と併用した場合

make_item() の確認 3

UrlView 0.9: (20 matches) Press Q or Ctrl-C to Quit!

->    1 http://japanese.engadget.com/2008/12/10/qwerty-grippity/
      2 http://japanese.engadget.com/category/peripherals/
      3 http://japanese.engadget.com/category/weird/
      ...

の先頭のリンクがエントリへのリンクとなるよう生成しています。

というところで今回はここまで。使いながら気になるところなど少しずつ手を入れていこうと思います。

追記(2009/01/07付)

feed_to_mailbox.py につき、動作確認の結果を元に新たな版を書きました。追記の形で書くには変更が多いため、あらためて以下のエントリ

フィードの更新差分を取得 - まとめ

に掲載します。

PageTop

コメント


管理者にだけ表示を許可する