Λάδι Βιώσας

http://profile.hatena.ne.jp/kenkitii/

はてなの質問に答えてみる

idで指定したはてなダイアリーの記事一覧を表形式で取得したい
http://q.hatena.ne.jp/1220786470

こんな感じ↓のスクリプトになりました。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random
import datetime, time, sys
import base64, sha
import httplib
from optparse import OptionParser
from BeautifulSoup import BeautifulSoup

class AtomClient:
    def __init__(self):
        self.endopoint = None
        self.wsse = None

    def credentials(self, endpoint, user, password):
        nonce = sha.sha(str(time.time() + random.random())).digest()
        now = datetime.datetime.now().isoformat() + "Z"
        digest = sha.sha(nonce + now + password).digest()

        wsse = 'UsernameToken Username="%(u)s", PasswordDigest="%(p)s", Nonce="%(n)s", Created="%(c)s"'
        value = dict(u = user, p = base64.encodestring(digest).strip(),
                     n = base64.encodestring(nonce).strip(), c = now)

        self.endpoint = endpoint
        self.wsse = wsse % value

    def atom_request(self, method, URI, body):
        con = httplib.HTTPConnection(self.endpoint)
        con.request(method, URI, body, {'X-WSSE' : self.wsse, 'Content-Type':'text/xml'})
        r = con.getresponse()

        response = dict(status = r.status,
                       reason = r.reason,
                       data   = r.read())
        con.close()
        return response

class DiaryList(AtomClient):
    def __init__(self, id, password):
        self.id = id
        self.current_page = 0
        self.credentials('d.hatena.ne.jp', id, password)

    def get_feed(self, start, end):
        results = []
        entries = self.next_page()
        while entries:
            for e in entries:
                now = e.published.contents[0][:10].replace("-", "")
                if now < start: return results
                if now <= end: results.append(e)
            entries = self.next_page()

        return results

    def next_page(self):
        uri = '/%s/atom/blog?page=%i' % (self.id, self.current_page)
        self.current_page += 1
        response = self.atom_request('GET', uri, "")
        entries = BeautifulSoup(response['data']).findAll('entry')
        return entries

def str2date(s):
    try:
         return datetime.datetime.strptime(s, "%Y%m%d")
    except ValueError:
        print "date data did not match format: %Y%m%d"
        sys.exit()

def main():
    usage = "usage: %prog id password [options]"
    parser = OptionParser(usage)
    parser.add_option("-s", "--start", dest="start", help='start date', metavar="DATE")
    parser.add_option("-e", "--end", dest="end", help='end date', metavar="DATE")
    (options, args) = parser.parse_args()
    if len(args) != 2:
        parser.error("invalid arguments")

    end = datetime.datetime.now().strftime("%Y%m%d")
    if options.end: end = options.end
    start = (str2date(end) - datetime.timedelta(30)).strftime("%Y%m%d")
    if options.start: start = options.start
    if str2date(start) >= str2date(end):
        parser.error("invalid dates")

    d = DiaryList(args[0], args[1])
    entries = d.get_feed(start, end)
    for e in entries[::-1]:
        date = e.published.contents[0][:10].replace("-", "")
        title = e.title.contents[0]
        link = e.find("link", {"rel": "alternate"})['href']
        print "%s,%s" % (link, title)

if __name__ == "__main__":
    main()

インストール方法

python 2.5以上をインストールします。上記のスクリプトは、BeautifulSoupというxmlパーサーを利用しているので、ここからダウンロードして、上記のスクリプトを保存した場所と同じフォルダに置いておきます。

使い方

はてなダイアリーAtomPubを使ってる都合上、はてなIDとパスワードが必要になります。日付は、20080911のようなフォーマットで指定します。日付を指定しない場合、最新1ヶ月分の日記のタイトルを取得します。

C:\python2.5>python diarylist.py -h
Usage: diarylist.py id password [options]

Options:
  -h, --help            show this help message and exit
  -s DATE, --start=DATE
                        start date
  -e DATE, --end=DATE   end date

使用例

C:\python2.5>python diarylist.py kenkitii ****** -s 20060101 -e 20060301
http://d.hatena.ne.jp/kenkitii/20060105/p1,[Web]YouTube
http://d.hatena.ne.jp/kenkitii/20060110/p1,[web]楽天祭り
http://d.hatena.ne.jp/kenkitii/20060111/p1,[etc][Google]宮崎駿
http://d.hatena.ne.jp/kenkitii/20060113/p1,[XBOX][360]Project Gotham Racing 3
http://d.hatena.ne.jp/kenkitii/20060117/p1,[news]Livedoor
http://d.hatena.ne.jp/kenkitii/20060119/p1,[etc]e-typingの腕試しチェック
http://d.hatena.ne.jp/kenkitii/20060121/p1,[column]勉強しなくていいよ、という人
http://d.hatena.ne.jp/kenkitii/20060121/p2,[book][python]Python入門
http://d.hatena.ne.jp/kenkitii/20060123/p1,[etc]なんか
http://d.hatena.ne.jp/kenkitii/20060126/p1,[Game][http://pcweb.mycom.co.jp/news/2006/01/26/008.html:title]
http://d.hatena.ne.jp/kenkitii/20060126/p2,[hatena]最近ハテナが妙に重いんだけど
http://d.hatena.ne.jp/kenkitii/20060204/p2,ぼくはまちちゃん! 
http://d.hatena.ne.jp/kenkitii/20060207/p1,[book]コレ買った
http://d.hatena.ne.jp/kenkitii/20060207/p2,[book]えー
http://d.hatena.ne.jp/kenkitii/20060208/p1,[book]富士通成果主義の崩壊
http://d.hatena.ne.jp/kenkitii/20060210/p2,[column]日本語の曖昧さ
http://d.hatena.ne.jp/kenkitii/20060215/p1,スタックオーバーフロー
http://d.hatena.ne.jp/kenkitii/20060216/p1,[python]はまりごと
http://d.hatena.ne.jp/kenkitii/20060217/p1,[etc]突っ込み先
http://d.hatena.ne.jp/kenkitii/20060223/p1,[python]運用管理ツール
http://d.hatena.ne.jp/kenkitii/20060227/p1,[Python]MeCabとPythonで遊んでみたメモ
http://d.hatena.ne.jp/kenkitii/20060301/p1,[Python]MeCabとPythonで遊んでみたメモ2

はてなハイクの人力シムシティをフラッシュアニメにするスクリプト

jkondoさんの今日の日記を読んでいたら、人力シムシティなるものが紹介されてました。これはおもろい!けど、一枚一枚を画像でみるんじゃなくて、パラパラ漫画みたいにしてみたいなあ、、、と思ったので、pythonflashアニメを作るスクリプトを書いてみました。

  • haiku-anime.py
import os
import re
import urllib2
from subprocess import *
from optparse import OptionParser

from ming import *

PATH_TO_FOTO = 'fotos'

def extract_fotos(url):
    links, pages_links = [], []
    query, page = url, 1
    foto = re.compile('img src="(http://f.+?)"')

    while True:
        links = []
        for line in urllib2.urlopen(query):
            r = foto.search(line)
            if r:
                links.append(r.group(1))

        if links == []: break
        pages_links += links
        page += 1
        query = url + "?page=%i" % page

    return pages_links[::-1]

def download_foto(url):
    if not os.access(PATH_TO_FOTO, os.F_OK):
        os.mkdir(PATH_TO_FOTO)

    dat = urllib2.urlopen(url).read()
    filename = os.path.basename(url)
    open(os.path.join(PATH_TO_FOTO, filename), "wb").write(dat)
    return filename

def png2dbl(filename):
    path_to_file = os.path.join(PATH_TO_FOTO, filename)
    call(["png2dbl", path_to_file])
    return filename[:-3] + 'dbl'
    
def create_movie(dbls, swf_name):
    X_MARGIN, Y_MARGIN = 20, 20

    m = SWFMovie()
    m.setRate(1.0)
    image = SWFBitmap(os.path.join(PATH_TO_FOTO, dbls[0]))
    swf_size_x = image.getWidth() + X_MARGIN * 2 
    swf_size_y = image.getHeight() + Y_MARGIN * 2
    m.setDimension(swf_size_x, swf_size_y)

    for dbl in dbls:
        item = m.add(SWFBitmap(os.path.join(PATH_TO_FOTO, dbl)))
        item.moveTo(X_MARGIN, Y_MARGIN)
        m.nextFrame()
        m.remove(item)

    m.save(swf_name)

def main():
    usage = "usage: %prog url [options]"
    parser = OptionParser(usage)
    parser.add_option("-o", "--output", dest="output", help='output filename.')
    (options, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("invalid arguments")

    output = "test.swf"
    if options.output: output = options.output
    images = extract_fotos(args[0])
    pngs = map(download_foto, images)
    dbls = map(png2dbl, pngs)
    create_movie(dbls, output)
    print "done."

if __name__ == "__main__":
    main()

上記のスクリプトでは、mingというflashコンテンツを生成するライブラリを使っているので、動作にはmingが必要です。http://sourceforge.net/projects/ming から、ming-0.3.0.tar.gz, ming-py-0.3.0.tar.gz を取ってきましょう。ちなみに、macosxの場合、以下のような手順でpythonからmingを使えるようになります。

sudo port install jpeg libpng libungif zlib 
tar zxvf ming-0.3.0.tar.gz
tar zxvf ming-py-0.3.0.tar.gz
cd ming-0.3.0 
./configure "LDFLAGS=-L/opt/local/include -L/opt/local/lib" "CPPFLAGS=-I/opt/local/include -I/opt/local/lib" "CFLAGS=-I/opt/local/include -I/opt/local/lib" 
make 
sudo make install 
cd py_ext
python setup.py build
sudo python setup.py install

使ってみる

こんな感じ↓で使うと、

python haiku-anime.py http://h.hatena.ne.jp/keyword/%e4%ba%ba%e5%8a%9b%e3%82%b7%e3%83%a0%e3%82%b7%e3%83%86%e3%82%a3 -o simcity.swf

こんな感じのswfファイルが生成されます。ちなみに生成元にした、はてなハイクコレです。てか、同じ画像サイズのものでないと、うまくアニメになりませんね、、、

mixiの更新情報をRSSで取得

ネタがないので昔書いたスクリプトを貼ってみます。

mixiの更新情報をRSSで取得したいなーと思って、mixipressを使ってみたところ動かなかったので、mixi stationAPIを使って簡単に書きました。認証にはcookieを使ってます。

#!/usr/bin/env python
# -*- coding: euc-jp -*-

import urllib, urllib2, cookielib

username = 'mixiのメールアドレス'
password = 'mixiのパスワード'
mixiid = 'mixiのid(プロフィール欄にある475205みたいな数字)'

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
opener.addheaders = [('User-agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)')]
params = urllib.urlencode({'email': username, 'password': password, 'next_url': 'home.pl'})

handle = opener.open('http://mixi.jp/login.pl', params)
try:
    print "Content-type: text/xml\n"
    print opener.open('http://mixi.jp/atom/updates/r=1/member_id=%s/-/diary' % mixiid).read()
except urllib2.HTTPError, e:
    print "Content-type: text/html\n\n"
    print "Error: %s" % e.read()

pythonが動く適当なサーバーにスクリプト置いて、RSSリーダーに登録すれば使えます。

世界同時株安

FRBの緊急利下げを受けて、今日の日経は若干戻しましたね。しかし、年明けからの下げはほんとひどい。なんだか日本がサブプライム問題の影響を一番受けていて、世界同時株安の牽引役になってる気がしてなりません。そんなわけで、サブプライム問題が出始めた、ここ半年の各国の市場の株価騰落率を求めてみました。

#! -*- coding: utf-8 -*-
import urllib2, csv

markets = {
    "^N225": "日経",
    "^DJI": "ダウ",
    "^IXIC": "ナスダック",
    "000001.SS": "上海",
    "^KS11": "韓国",
    "^TWII": "台湾",
    "^HSI": "香港",
    "^STI": "シンガポール",
    "^AORD": "オーストラリア",
    "^BSESN": "インド",
    "^FTSE": "エゲレス",
    "^FCHI": "フランス",
    "^GDAXI": "ドイツ",
    "^IBEX": "スペイン",
    "^AEX": "オランダ",
    "^GSPTSE": "カナダ",
    "^MXX": "メキシコ",
    "^BVSP": "ブラジル",
    "^MERV": "アルゼンチン",
    }

def get_prices(code):
    print code
    url = "http://ichart.finance.yahoo.com/table.csv?s=%s&a=06&b=23&c=2007&d=00&e=23&f=2008&g=d&ignore=.csv"
    dat = [row for row in csv.reader(urllib2.urlopen(url % code))]
    return {"period": "%s~%s" % (dat[-1][0], dat[1][0]), "start": float(dat[-1][4]), "end": float(dat[1][4])}

results = []
for code, market in markets.items():
    prices = get_prices(code)
    results.append((market, prices["end"]/prices["start"]-1, prices["period"]))

results.sort(lambda x, y: cmp(x[1], y[1]))
print "|*市場|*騰落率(%)|*期間|"
for r in results:
    print "|%s|%s|%s|" % (r[0], "%.2f" % (r[1]*100), r[2])

こんな感じの結果がでました。

ここ半年の各市場の株価騰落率

市場 騰落率(%) 期間
日経 -30.01 2007-07-23~2008-01-22
オランダ -22.49 2007-07-23~2008-01-22
台湾 -21.20 2007-07-23~2008-01-22
フランス -19.41 2007-07-23~2008-01-22
韓国 -19.27 2007-07-23~2008-01-22
オーストラリア -18.76 2007-07-23~2008-01-22
メキシコ -16.40 2007-07-23~2008-01-22
カナダ -16.15 2007-07-23~2008-01-21
アルゼンチン -15.62 2007-07-23~2008-01-22
ナスダック -14.80 2007-07-23~2008-01-22
ドイツ -14.79 2007-07-23~2008-01-22
スペイン -14.72 2007-07-23~2008-01-22
ダウ -14.14 2007-07-23~2008-01-22
エゲレス -13.35 2007-07-23~2008-01-22
シンガポール -8.00 2007-07-23~2008-01-09
香港 -6.88 2007-07-23~2008-01-22
ブラジル -3.34 2007-07-23~2008-01-22
インド 6.34 2007-07-23~2008-01-22
上海 16.64 2007-07-23~2008-01-21

ぶっちぎりで日経ですね、、、。国に対してなんとかしろとかいうつもりはないですが、「日本へのサブプライム問題の影響は限定的で、当面は見守っていくのが一番いい」とかいってる首相は危機感無さ過ぎだと思います。ほんとなんとかしろ!!!

指定した株銘柄と似た値動きをするものを探すpythonスクリプト

昨日のエントリーで勢いで書いたスクリプトは、いまいち使えない&汚いので改良してみました。改良したスクリプトココに置いてあります。直近の25日間で、指定した株の銘柄と似たような値動きをしている他の銘柄を検索することができます。株価のデータは、無尽蔵というサイトのものを使わせて頂きました。ありがとうございます。

使い方

まず株価データを取ってくる為に、

find_similar_stock.py -d

として起動します。直近の25日分の株価データをダウンロードします。その後、

find_similar_stock.py 銘柄コード

とする事で、指定した銘柄と似たものを検索してくれます。

あ、言い忘れました。昨日と同じくmacbookで動作検証を行っており、スクリプト内部で、lhaコマンドを使っているので、入れてない方は、sudo port install lha してください。ちなみに、スクリプトを置いたフォルダの直下にdatというフォルダを作り、その中に無尽蔵サイトにある25日分の株価csvファイルを入れとけば、windowsでも動きます。たぶん。

使ってみる

それじゃ、どんな感じで動くか試してみます。まず、、、日本が誇る消費者金融の会社であるアイフルと似た値動きの銘柄を検索してみます。アコム、プロミス、武富士あたりが来るんじゃないかと予想しますが、、、

% python find_similar_stock.py 8515
8574 プロミス 0.956341898393
5985 サンコール 0.93753693809
2384 エスビーエス 0.931599905915
2461 ファンコミ 0.931509636374
2497 NGIグループ 0.928936369522
2459 アウンコン 0.927707540843
7841 遠藤製作所 0.923338287339
4838 Sシャワー 0.918653656434
2488 日本サード 0.918631347172
2413 ソネットエム 0.916991622729

予想通り、プロミスが来ました。ちなみに右端の数字は検索対象銘柄との相関係数です。1に近いほど似たような値動きをしているということですね。2007年10月28日現在のチャートを並べてみるとこんな感じ。左アイフル、右プロミスです。

スクリプトでやってることは、単に相関係数を求めてソートしてるだけですけど、日経平均連動銘柄を探したりなどなど、結構使えそうな感じです。

Pythonで簡易銘柄スクリーニング

最近、ピアキャストにも飽きてきて、株価のチェックが趣味になってきました。あちこちのサイトをみたり、omega chart を使ってスクリーニングしてみたりと、色々遊んでるわけですが、もうちょっとこうなんていうか、自由にいろいろできるようにならんもんかなぁ、と思い始めたので、ちょっとしたスクリプトを書いてみました。

  • stock.py
#! -*- coding: cp932 -*-
import sys, os
from subprocess import Popen, call
from datetime import datetime, timedelta
import urllib2

class Storage(dict):
    def __getattr__(self, key): 
        if self.has_key(key): 
            return self[key]
        raise AttributeError, repr(key)
    def __setattr__(self, key, value): 
        self[key] = value
    def __repr__(self):     
        return '<Storage ' + dict.__repr__(self) + '>'

class Stocks(dict):
    path_to_dat = "dat"

    def __init__(self):
        self._make_dict()

    def _execute(self, cmd):
        status = True
        try:
            ret = call(cmd, shell=True)
            if ret < 0:
                print >>sys.stderr, "The child process was exited by signal.", -ret
                status = False
        except OSError, e:
            print >>sys.stderr, "failed to execute.:", e
            status = False
        return status

    def _download(self, date):
        filename = "t%s.csv" % date.strftime("%y%m%d")
        if not self.path_to_dat in os.listdir("."):
            os.mkdir(self.path_to_dat)
            
        if filename in os.listdir(self.path_to_dat):
            return True

        cmd = "curl -O --silent http://souba-data.com/data_day/%sd/%s_%sd/T%s.lzh" % (
            date.strftime("%Y"),
            date.strftime("%y"),
            date.strftime("%m"),
            date.strftime("%y%m%d"))

        if not self._execute(cmd):
            return False
            
        cmd = "lha eq T%s.lzh" % date.strftime("%y%m%d")
        if not self._execute(cmd):
            return False

        self._execute("mv t%s.csv dat/" % date.strftime("%y%m%d"))
        self._execute("rm T%s.lzh" % date.strftime("%y%m%d"))
        return True
    
    def _make_dict(self):
        yesterday = datetime.today() - timedelta(1)
        while yesterday.weekday() == 5 or yesterday.weekday() == 6:
            yesterday = yesterday - timedelta(1)
            
        filename = "T%s.csv" % yesterday.strftime("%y%m%d")
        if not self._download(yesterday):
            print >>sys.stderr, "Error: failed to download stock data."
            sys.exit(1)
        
        dat = open(os.path.join(self.path_to_dat, filename)).read()
        
        markets = {'東証1部': 'T1', '東証2部': 'T2', 'ヘラクレス': 'H', '大証ヘ': 'H',
                   'JAQ': 'J', '大証1部': 'O1', '大証2部': 'O2', '東証マ': 'M',}
        markets_rss = {'東証1部': 'T', '東証2部': 'T', 'ヘラクレス': 'OJ', '大証ヘ': 'OJ',
                       'JAQ': 'Q', '大証1部': 'OS', '大証2部': 'OS', '東証マ': 'T', }

        for line in dat.strip().split("\n"):
            columns = line.strip().split(",")
            #2007/10/10,1001,11,1001 日経225,17231,17255,17146,17178,1693630000,東証1部
            #1          2    3  4              5     6     7     8     9          10
            if columns[9] in ['外国', '名古1部', '名古2部', '名証セ']:
                continue
            if columns[1] == '1005':
                continue

            self[int(columns[1])] = Storage({
                'description': unicode(columns[3][5:],'cp932').encode('utf-8'),
                'market': markets[columns[9]],
                'opening_price': int(columns[4]),
                'high_price': int(columns[5]),
                'low_price': int(columns[6]),
                'closing_price': int(columns[7]),
                'volume': float(columns[8]),
                'rss_code': "%s.%s" % (str(columns[1]), markets_rss[columns[9]])})
  • 必要なもの

lhaを呼び出しているので、sudo port install lha しといてください。あ、言い忘れましたが、Macで動作チェックをしています。スクリプトの中で、curlとlhaを呼び出していますが、ここらへん修正すれば windowsでも動くと思います。たぶん。


で、、、こんな感じで使えます。指定した銘柄の、昨日の4本値と出来高を取得できます。

>>> import stock
>>> s = stock.Stocks()
>>> s[4755]
<Storage {'high_price': 57500, 'volume': 197372.0, 'description': '\x8ay\x93V', 'low_price': 53000, 'opening_price': 53500, 'rss_code': '4755.Q', 'closing_price': 57500, 'market': 'J'}>
>>> print s[4755].description
楽天
>>> s[4755].closing_price
57500
>>> 
>>> for code, v in s.items():
...   if 0 < v.closing_price < 200:
...     print v.description
... 
中川無線
さが美
ラオックス
ステラG
・・・(以下略)

スクリーニングなら omega chartで色々出来るんですが、まぁpythonでやってみたかったと。。。台風来てて暇だし。。。というか、omega chartの拡張の書き方がよくわからず挫折気味です。

Mac用 実況向けスレッド型掲示板ビューアー Palloo

peercast を見ていると、実況向けの掲示板ビューアが必須な感じになります。自分は実況向けのビューアとして、Windowsでは Balloo と Jane を併用していますが、Macには実況向きのビューアがありません。(あるのかもしれないけど、よく知りません。。。)くそう。。。

そんなわけで、Macでも動くPython製Ballooもどき?な Palloo を作ってみました。

動作イメージ&特徴

python から Growl を呼び出して通知画面を表示してます。こんな感じ↓です。

アイコンがドーモ君なのは気にしないでください。対応掲示板は、2ちゃんねる、JBBS、YY Kakikoです。板の中から特定の文字列を含むスレッドを抽出し、その中で最も勢いのあるスレッドを監視することができます。また、同時に複数のスレッドを監視することも出来ます。書き込み機能はありません。

スクリプトのダウンロード

ココからダウンロードできます。パスの通った適当なフォルダにぶち込んでください。

インストール方法

Mac Ports をインストールして、

sudo port install python25

とします。Mac Portsを入れてない人は、このあたりを見てインストールすることをお勧めします。

sudo port install py-yaml

します

http://growl.info/downloads.php
から、Growl 1.1.1 をダウンロードしてインストールします。

http://growl.info/downloads_developers.php
から、Growl 1.1.1 SDKをダウンロードして展開し、/Binding/python に移動し、

python setup.py install

します。

使用方法

config.yaml.sample を config.yamlにリネームし、コマンドラインから python Palloo.py と入力します。設定方法は、config.yaml.sample に詳しく書いてあります。それでは、Macで良いピアキャスライフを!!

・・・とかいいながら、自分は VMWare上のXPでピアカスみてたりして、、、