5/21に運よく(運悪く)小田急線で人身事故が起きたため、小田急線の運行情報をゲットするスクリプトが書けた。
import lxml.html
import requests
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')
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使用率を食うこともない。
import configparser
conf = configparser.ConfigParser()
conf.read('plugins.ini')
pluginDict = {}
for item in conf.items('plugins'):
vals = item[1].split('|')
pluginDict[item[0]] = vals
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)
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')
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 = ""
if val in result.keys():
item = result[val]
if (val in contentsDict.keys() and contentsDict[val] != item["contents"]) or val not in contentsDict.keys():
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)
time.sleep(60)
なお、まだメール送信処理は未実装。コードはできているが、コンソールでそれなりに動作することが分かったらメール送信機能も付ける。これで、運行情報通知プログラムはほぼ完了だ。外出時に人身事故が起きても駅についてびっくりということがなくなる。来週はメール送信処理をつけて、さらにニュースアプリについても実装を開始しよう。
pythonのインデント強制な書式の利点の一つは、構造化プログラムでよくやる中カッコの閉じ忘れが原理的に発生しないところだ。これはありがたい。でもインデントを1つミスると予期しない処理をしてしまう危険性とも隣り合わせだね。
もう一つ。はてなブログでははてな記法モードを使わないとソースコードが綺麗に表示できないことが不便だ。見たままモードにもスーパーpre機能をつけてほしい。