0

モバイルSuicaの履歴をtwitterに流したかった

xtunnelのためにスクレイピングの勉強をしていて、Mechanize+hpricotからMechanize+nokogiriの組み合わせに乗り換えようと色々と使ってみている中でできた物のひとつ。
昔しゃお先生がやっていたのを俺もやりたくて3ヶ月ぐらい前に作ったけど、mobilesuica.comのおサイフケータイ使用履歴は1日一度早朝に更新される仕様に変更されたらしくボツになった。


結局idやcssなどの手がかりが無くて手動で要素を取り出す事になり、nokogiriはHTMLタグを除去するのにしか使わなかった
MobileSuica.rb

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rubygems'
require 'nokogiri'
require 'mechanize'
require 'kconv'

module MobileSuica
  def MobileSuica.get(user,pass)
    agent = WWW::Mechanize.new
    agent.user_agent_alias = 'Windows IE 7'
    page = agent.get('http://www.mobilesuica.com/iq/ir/SuicaDisp.aspx?returnId=SFRCMMEPC03')
    login_form = page.forms_with(:name => 'form1').first
    login_form.fields_with(:name => 'MailAddress').first.value = user
    login_form.fields_with(:name => 'Password').first.value = pass
    page = login_form.click_button
    
    return page.body.toutf8.split(/<tr>/).delete_if{|tr|
      !(tr =~ /&yen;/m)
    }.map{|tr|
      tr.gsub(/\n/,"").split(/\r/)[0..5].map{|line| # 月日,種別,利用場所,種別,利用場所,残額
        Nokogiri(line).inner_text.chomp.strip.gsub(/[\t ]/,"")
      }
    }
  end
end


mobilesuicaのユーザ名、パスワードで履歴を2次元配列として取り出せる。
require 'MobileSuica'
MobileSuica.get("user", "pass")
月日、種別、利用場所、種別、利用場所、残額の順になる
01/30

川崎

横浜
5,110
01/30

相鉄横浜
窓出
川崎
5,320



履歴のうち最新の駅名をtwitterに投稿する。-dつけて起動するとdaemonになる
tweet-mobilesuica.rb
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rubygems'
require 'webrick'
require 'twitter'
require 'MobileSuica'

def start(conf)
  loop do
    begin
      first = MobileSuica.get(conf["suica_user"], conf["suica_pass"]).first
    rescue
      first = nil
    end
    if first != nil
      last = first if !last
      if first != last # 1回前に取得した履歴と比較
        puts first
        puts '-'*10
        if first[1] == '入' && first[3] == '出' # 降車履歴の時
          message = first[4].chomp.strip+"なう (suica)"
          if !(message =~ /#{conf["ngwords"]}/)
            if conf["nopost"] != true
              httpAuth = Twitter::HTTPAuth.new(conf["twitter_user"], conf["twitter_pass"])
              tw = Twitter::Base.new(httpAuth)
              tw.update(message) # twitter post
            end
            puts message
          end
        end
        last = first
      end
    end
    sleep 60*60*1.5 # 1時間半待つ
  end
end

conf = YAML::load open File.dirname(__FILE__)+'/config.yaml'
if ARGV[0] == '-d'
  WEBrick::Daemon.start {
    start(conf)
  }
else
  start(conf)
end


設定ファイル。自宅の駅名などはngwordsに入れておく
config.yaml
# config.yaml
# mobilesuica.com user/pass
suica_user : 'username@docomo.ne.jp'
suica_pass : '12345678'

# twitter user/pass
twitter_user : 'shokai'
twitter_pass : 'password'

# postしない駅名を正規表現で
ngwords : "(東京|横浜)"

# for debug
#nopost : "true"

0

使ってるchrome extensions

LDRにいろいろ付ける系のgreasemonkeyはGM_系の関数がまだ実装されてないので、LDRだけはFirefoxで見ている。
テキストエリアを外部テキストエディタで開くIt’s all text相当の拡張もあったけど、server側のインストールやら起動が面倒なのでblog書くのはFirefox使う。

普段の調べ物とSBMへの投稿、twitterぐらいならこれらを入れてあれば十分。一番重要なのはTaberarelooでdeliciousとはてなブックマークとlivedoor clipにすばやくクロスポストができる事。

意外なことにchrome keyconfigにLDRizeが入っている。AdBlock+は要素の指定がなんか面倒くさい。

pbtweet、chrome FullFeed、fldr_show_fc2_images.user.jsはMacはdev版のchromeなら使える。

GM_系が一通り実装されるのと、URLバーの履歴検索がFirefoxみたいに一部一致でも利用頻度の高い順にソートされて表示されるようになればFirefoxから乗り換えられるな。
chromeのURLバーは前方一致が優先されるので、例えばりそな銀行のログインページに行きたい場合、Firefoxだと「res」まで打てば候補にすぐ出てくるが、chromeなら「ib.res」まで打ち込まないと https://ib.resonabank.co.jp/IB/0102/SC_N_0102_010.aspx が出てこない。ibとか思い出せない。「りそな→reso」なら連想できる。URLがおかしい気もするが、サービス毎にサブドメイン振っているケースはいっぱいあるし、サブドメインは覚えられない。 https://e-navi.rakuten-kc.co.jp/ava_top とか。
chrome extensions

0

wavファイルの音量を調整する

音量の小さいwavファイルのボリュームを上げる。上げすぎて音割れしないようにする。

前に作ったWavFile.rbを使ったら簡単にできた


16ビットwavは+-32768、8ビットwavは+-128の範囲の配列で波形が表現されている。
ソースのwavの波形を配列に取り出して、その中で最大の値を取りだし、全体を何倍すれば+-32768の間になるかの倍率を計算して全部かけ算すれば音量を調整できる。

maximizeVolume.rb
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# wavの音量を最大に調節する
require File.dirname(__FILE__) + '/WavFile'

if ARGV.size < 2
  puts 'ruby maximizeVolume.rb input.wav output.wav'
  exit 1
end

in_file = ARGV.shift
out_file = ARGV.shift

format, data = WavFile::read open(in_file)

puts format.to_s

bit = 's*' if format.bitPerSample == 16 # int16_t
bit = 'c*' if format.bitPerSample == 8 # signed char
wavs = data.data.unpack(bit)

puts "このwav中の最大音量: #{wavs.max}"

volume_ratio = 32768/wavs.max.to_f if format.bitPerSample == 16
volume_ratio = 128/wavs.max.to_f if format.bitPerSample == 8
puts "補正倍率: #{volume_ratio}"

wavs_fixed = wavs.map{|w|
  (w*volume_ratio).to_i
}
puts "補正されたwav中の最大音量: #{wavs_fixed.max}"

data.data = wavs_fixed.pack(bit)

open(out_file, "w"){|out|
  WavFile::write(out, format, [data])
}


使う
ruby maximizeVolume.rb input.wav out.wav

約24倍されてout.wavに保存された
フォーマットID: 1
チャンネル数: 1
サンプリングレート: 44100 (Hz)
byte per sec: 88200
bit per sample: 16
ブロックサイズ: 2
このwav中の最大音量: 1335
補正倍率: 24.5453183520599
補正されたwav中の最大音量: 32768

0

json_builderを特殊文字のエスケープ、true、false、nullに対応させた

前:橋本商会 C++でmapやvectorをJSON出力するjson_builder.hを作った


ダブルクオートなどを含む文字列を値に保持するためのエスケープ処理にboost::regexを使ったので、libboost_regex-mt.aをコンパイル時に読み込まないとならなくなった → Makefileの例
まさかboost::regex_replaceで頭にバックスラッシュをつけるのに、バックスラッシュ4つで置換するとは思わなかった


こんな風に使う。true, false, nullを入れられるようになった
test.cpp

#include <iostream>
#include <string>
#include <map>
#include <boost/any.hpp>
#include "../json_builder.h"

int main(int argc, char* argv[]){
  std::map<string,boost::any> user;
  user["name"] = std::string("shokai");
  user["fullname"] = std::string("sho hashimoto");
  user["age"] = 25;
  user["test"] = 1.23;
  user["null"] = json_builder::null;
  user["true"] = true;
  user["false"] = false;

  string json = json_builder::toJson(user);
  cout << json << endl;
  return 0;
}


実行結果
{"age":"25","false":false,"fullname":"sho hashimoto","name":"shokai","null":null,"test":"1.23","true":true}



C++でnullを表現するために適当な構造体を定義してしまったけど、こういうので良いんだろうか?
json_builder.h
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <boost/any.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/format.hpp>
#include <boost/foreach.hpp>
#include <boost/regex.hpp>

using namespace std;
using namespace boost;

#define null json_null()

namespace json_builder{
  struct json_null{};

  string toJson(any value){
    if(value.type() == typeid(vector<any>)){
      string result_str;
      vector<any> vec = any_cast<vector<any> >(value);
      for(int i = 0; i < vec.size(); i++){
result_str += toJson(vec[i]);
if(i < vec.size()-1) result_str += ",";
      }
      result_str = str(format("[%s]") % result_str);
      return result_str;
    }
    else if(value.type() == typeid(map<string,any>)){
      string result_str;
      map<string,any> m = any_cast<map<string,any> >(value);
      string key;
      any value;
      int i = 0;
      BOOST_FOREACH(tie(key,value), m){
result_str += str(format("\"%s\":%s") % key % toJson(value));
if(++i < m.size()) result_str += ",";
      }
      result_str = str(format("{%s}") % result_str);
      return result_str;
    }
    else if(value.type() == typeid(json_null)){
      return string("null");
    }
    else if(value.type() == typeid(string)){
      return str(format("\"%s\"") % 
   regex_replace(any_cast<string>(value), regex("[\"\'\\\\/]"), "\\\\$0"));
    }
    else if(value.type() == typeid(bool)){
      if(any_cast<bool>(value)) return string("true");
      return string("false");
    }
    else if(value.type() == typeid(int)){
      return str(format("\"%d\"") % any_cast<int>(value));
    }
    else if(value.type() == typeid(double)){
      return str(format("\"%d\"") % any_cast<double>(value));
    }
  }

}

0

最近作ったYahooPipes

Pipes: 秋月電子 新製品FullFeed
秋月のRSSが無くなったので、新製品ページから取り出した。


Pipes: ストロベリー・リナックス FullFeed
strawberry-linux.comのnews feedがタイトルのみ配信だったので

どちらも商品イメージとデータシートへのリンクと価格を入れてある。

千石は元気の良いblogで新製品を教えてくれる。spark funと液晶工房もblogがある。スイッチサイエンスもフィード作ろうかと思ったけど新製品ページが半年ぐらい更新されてなかったからやめた。マルツもblogあるけど勉強会情報しかなくて、新製品情報はwebで一覧できない。電子工作系はいまだに新製品情報をネットで効率的に収集する手段が乏しいな。


Pipes: twitter list timeline
twitterのlist機能にrssが無いので。list出た時に作ったんだけど、twitterのHTMLが変わっていつのまにか動かなくなってたのを修理した。
ユーザ名とlist名を入れるとRSSを出す。他人の作ったlistをいろいろ購読してみてる。


pipesってソースのHTMLが変わっていつのまにかエラーになってるんだけど、RSSリーダに登録してるだけだと配信されなくなるだけで、「動かなくなった」事に気づけないんだよなあ
なんとかならないものか。1件も出力できなかったら一番上にエラー文を入れるようにすればいいのか?