Hinagikuのコードを読んでみたよメモ

PowerShellがパイプ経由でオブジェクトを渡すのにビックリしてしまったbonlifeです。
さてさて、チーズ臭えよ!でお馴染みのPyPI(Python Cheese Shop)のRSSをチェックしてたら、和的な名前のものがアップロードされていました。気になったのでちょっとチェックしてみましたよ。名前はHinagiku。とりあえずeasy_installでインストール。その後、importしてhelpしてみる。

In [1]: import hinagiku

In [2]: help(hinagiku)
Help on package hinagiku:

NAME
    hinagiku - Hinagiku is a web page update checker called the Antenna.

FILE
    c:\python25\lib\site-packages\hinagiku-0.0.0-py2.5.egg\hinagiku\__init__.py

PACKAGE CONTENTS
    core

DATA
    __author__ = 'Yamagishi Kazutoshi <nysox@nysox.jp>'
    __docformat__ = 'restructuredtext'
    __license__ = 'MIT'
    __url__ = 'http://hinagiku.overknee.org/'
    __version__ = '0.0.0'

VERSION
    0.0.0

AUTHOR
    Yamagishi Kazutoshi <nysox@nysox.jp>

AUTHORが明らかに日本人らしい名前で親近感。でも、使い方はほとんど分からなかったので、こちらをチェック。なんとなく分かったのは、config.yaml に設定情報を書いておくと、その内容を元にWebサイトの更新状況をチェックして、 template.html に従って表示してくれるっぽいこと。Pythonのパッケージの仕組みがまだよく分かってないのですが、Scriptsフォルダ("C:\Python25\Scripts")に hinagiku というファイルが出来てますね。Windowsで試そうとしたのですが、イマイチ使い方が分かりませんでした。Linuxとかだったらこのファイルを実行すれば良いのかしら。

python "C:\Python25\Scripts\hinagiku" -c config.yaml -t template.html -o index.html

こんな感じかな。違うかな。このあたりについては後でちょっと調べてみようと思います。このWindows環境でのスマートな実行の仕方がよくわからない hinagiku の中身をチェックすると from hinagiku.core import Hinagiku して、内部で Hinagikuクラスに OptionParser を駆使してオプションを渡して run() って関数を実行していました。ということで、次は Lib\site-packages 以下の hinagiku 関連っぽいフォルダの中身をチェック。
ファイルは以下の2つだけ。

  • __init__.py
  • core.py

__init__.py ではパッケージに関する情報をまとめているだけ。これがhelpで表示される情報になるのかしら。(とかそのあたりも後で調べます、はい。)
core.py の中で何をやっているのかが重要っぽい。あれこれ import した後、Hinagikuクラスの定義。基本はこれだけみたいですね。サラサラっと頭から読んでみて分かったのは以下の点。

  • Hinagikuクラスの __init__() で設定ファイルを読み込む
  • run() では crawl() と out() を順に実行
    • crawl() ではページの情報を取得して、output に格納
      • ignore_pattern() を呼び出して一部無視
      • get_last_modified() で date に設定する値を取得
    • out() では cache.yaml に何かしら書き込んだり、output_path で指定したファイルに date の降順でソートした結果を template を通して書き込んだり

で、どうなるのかと言うと、DEMOの通り。更新日の降順でWebサイトの情報が表示されてますね。これに、ページの一部の情報を取得するようにしたらはてなアンテナっぽいかな。
まだちゃんと全体を理解したわけではありませんが、勉強になった点とこういった書き方もあるんじゃないの、ってところを書いてみようと思います。

勉強になった点

  • 辞書から値を取り出す時、dict.get() して、Exceptionを発生させずに処理している

こういった書き方もあるんじゃないの、な点

  • 初期値の設定を or を使ってやっている部分は、引数のデフォルト値で渡せば良い

[15-18行目]

class Hinagiku:
  def __init__(self, config_path=None, template_path=None, output_path=None):
    self.flag = dict()
    self.config_path = config_path or "config.yaml"

こんな感じで or で初期値を渡してますが、以下のように __init__() の引数のデフォルト値にしてみてはいかがでしょう。

class Hinagiku:
  def __init__(self, config_path="config.yaml", template_path="template.xml", output_path="output.xml"):
    self.flag = dict()
    self.config_path = config_path
  • 変数の定義はまとめておいた方が見やすい (17行目の self.flag = dict()、31行目の self.output = list() など)
  • yaml.load() なども Exception が発生するのを想定しておいた方が良い(かも)
  • sort() した後、reverse() するのはもったいない

[62-63行目]

    self.output.sort(lambda x, y: cmp(x.get("date"), y.get("date")))
    self.output.reverse()

この部分は、以下のように書き直した方がスッキリするし速いはず。

    self.output.sort(lambda x, y: cmp(y.get("date"), x.get("date")))
  • スペルミス発見!

[81行目]

      d["ststus"] = "None"

これはこうですよね。

      d["status"] = "None"
  • あえて行を分けて書く必要がないものは一行にまとめる

[88-90行目]

      if last_modified:
        last_modified = parse(last_modified)
        last_modified = last_modified.isoformat()
      if last_modified:
        last_modified = parse(last_modified).isoformat()

こんな感じでいかがでしょう。
ちなみに、作者のid:ykztsさんの日記を読んでいたら、Hinagiku 0.0.1 がすでに完成しているみたいですね。スペルミスとかは直ってるかもかもです。私も何かしら作ってみたい衝動にかられましたが、何を作りたいのかも分からないし、スキルも不足していることに気づいてしまいましたよ…orz