2011-04-13

【地震】備えリスト

mixiの日記に書いたものの転載ですが。
3月11日の地震では、このブログのタイトル通り宮城県多賀城にいました。
とりあえず、私と家族全員は皆無事です。
3月11日から1ヶ月間に及ぶライフラインの完全ではない生活で分かった必要な「備え」についてをつらつら書いてみます。


---
友達が「備えリスト」を作ってたので真似してみるw 

復旧が早かったのが電気なので今回に限っては「オール電化住宅」がほしかったかなw 
水と電気があれば普通の生活が営めることに驚愕したね。 
ただ、その分電気がなければ「ガスが復旧してもどうにもならない」ことに注意w 

○水関係 
・飲料水をいれるポリ容器 
・焼酎のでっかい(5リットルくらいの奴)ペットボトル 
・ビニール製のゴミ袋 

 これはガチ。 
 無いと給水車から水もらってこれない。ゴミ袋は水瓶の中敷きにして水を貯めておいたりした。 
・収納ボックス的な物 
 大量の水を汲んでおける。これにトイレ用水を近くの小川から毎日バケツで運んでた。 
 例としてウチにあるのと同じもの:http://www.amazon.co.jp/dp/B000VLF6S4/ 
・バケツ(2~4個はあると便利)ゴム手袋 
 トイレを流すための水を調達するのにバケツが便利。10リットルで1回分流せる。 
 水汲みにいって濡れたりするのを防ぐためゴム手が便利。 
・近所で飲料水が汲める場所の情報 
 給水車がくるのはウチの近所では1日1~2回のみ。しかも1回につき5リットルまでだった。 
 なので、足りない分は湧水などでまかなうしかない。その情報を日頃から収集する。 
・近所でトイレ用の水が汲める場所の情報 
 飲料水をトイレに使うのがもったいないわけです。そこで自分は近所にある小川から 
 毎朝トイレ用水を汲みにいってた。 

○照明関係 
・ランタン付き懐中電灯 
 電球だけでは1方向しか照らせないので、ランタン機能がついてると広い範囲を照らせて便利 
・ろうそく 
 ガスもれが無い場合はこれを使う。倒れないように必ず工夫すること 
 ウチの2軒隣で地震直後にガスもれしてしまったので、火のでる物を使用できなかったので辛かった。 
・単3乾電池で点くペンライト 
 夜トイレに行くにも明かりが全くないので、ポケットに入れたまま寝てた。 

○食関係 
・粉の紅茶やインスタントコーヒー 
 飲み物が水しかなくなることへの対処 
・ラップ 
 言わずと知れた食器に敷く。洗わなくてもすむように。 
・「深い」ホットプレート 
 深いと煮込み系の料理ができる。カレーうどん作りまくりw 
 例として:http://www.amazon.co.jp/dp/B002HL4BFK/ 
・カセットコンロ 
 ウチでは電気の復旧が早かったのでホットプレートで代用。ほとんど使わなかった。 
 でもあると安心ではある 
・反射式石油ストーブと灯油 
 ウチの暖房器具は全て反射式石油ストーブ。 
 ご飯を炊いたり焼き物やお湯を沸かすのに便利。微妙に照明にもなるw 
 18リットルの石油ポリタンク6本ほどの灯油備蓄を日常的にしてたから助かったw 

○防寒関係 
・反射式石油ストーブ 
 上と同様防寒にも優れもの。 
・ホットカーペットや電気毛布 
 ウチにコタツがなかったので、上にホットカーペットに布団などをかけて温まる。 
 皆で入ればコタツ風味。電気が戻ったあと石油の節約に役立ちまくり。 

○安全関係 
・靴下やサンダル 
 床に割れたガラスや瀬戸物が氾濫するので、素足は危険。 
 とりあえず掃除機が使えるようになるまで必要。 
・ながーいマジックテープ(ハサミで切って使う) 
 兄が東京で仕入れて持ってきてくれた。 
 本棚をグルッと巻いたり、観音開きの食器棚をロックするのに便利 
・電池式ワンセグテレビ 
 近所の人がもってて、見せてもらいにいったりしたw 情報収集に役立った。 

○その他 
・自転車 
 車は本当に必要な時しかのらないようにしてる。ガソリンがもったいないから。 
 そこで自転車。あるいは原付のカブがあれば非常に役立つかもしれない。 
・手回し携帯充電器 
 携帯の電池は重要。これが今欲しいw 
・毛糸の帽子 
 基本的にお風呂にはいれないので髪の毛がベタベタになる。恥ずかしい場合は帽子で隠そうw 
・アルコールが使用されたウェットティッシュ 
 手が洗えないので。 
・ポケットラジオ(単三乾電池のものがいい) 
 単一電池は懐中電灯に使いたいので、ラジオは小型でスピーカー付きのを選ぶといいかも 
・リュックサック 
 買い物や配給される食料をいれるのに必要。買い物にいってもお店はポリ袋をくれないんだよね。 
・携帯用小型の椅子 
 買い物やガソリンスタンドで並ぶときは便利。ウチの近所では平気で4~5時間並ばされたりしたし。 
・ガソリン携行缶(重要!) 
 震災直後のガソリンスタンドには、車で乗り入れられないことが多い。 
 しかも「携行缶もってこないと売らないよ」ということが多い。 

後は気づいたときに書きますね。

2011-03-03

【AmazonAWS】商品に付いたBrowseNodesのパース方法

AmazonAWSのItemLookupのResponseGroupにBrowseNodesを入れると、そのItemが属してるカテゴリを引っ張ってこれる。
でもこのXMLが結構複雑でなかなか手をつけられないでいたのだけど、思いのほか簡単な方法で何とかなったのでそのときのメモ。ちなみにそのときのサイトは「多賀城屋書店」というサイトで携帯サイトだった。

今回は例として「涼宮ハルヒの驚愕」を使って解説してみる。



AWSのBrowseNodesは下の例のような<BrowseNode></BrowseNode>が「複数」あることと、一番上にある階層のカテゴリがXML入れ子の一番深いところにあるのが特徴。
下の例だと「本→Featured Categories→文庫総合→ライトノベル→男性向け→角川スニーカー文庫」というならびになる。


--XML例はじめ--

<BrowseNode>
  <BrowseNodeId>2220015051</BrowseNodeId>
  <Name>角川スニーカー文庫</Name>
  <Ancestors>
    <BrowseNode>
      <BrowseNodeId>2189055051</BrowseNodeId>
      <Name>男性向け</Name>
      <Ancestors>
        <BrowseNode>
          <BrowseNodeId>2189052051</BrowseNodeId>
          <Name>ライトノベル</Name>
          <Ancestors>
            <BrowseNode>
              <BrowseNodeId>2189048051</BrowseNodeId>
              <Name>文庫総合</Name>
              <Ancestors>
                <BrowseNode>
                  <BrowseNodeId>202188011</BrowseNodeId>
                  <Name>Featured Categories</Name>
                  <Ancestors>
                    <BrowseNode>
                      <BrowseNodeId>465392</BrowseNodeId>
                      <Name>本</Name>
                    </BrowseNode>
                  </Ancestors>
                </BrowseNode>
              </Ancestors>
            </BrowseNode>
          </Ancestors>
        </BrowseNode>
      </Ancestors>
    </BrowseNode>
  </Ancestors>
</BrowseNode>

--XML例終わり--


タグの解説をすると、
・<BrowseNodeId>はカテゴリIDで数字のデータ
・<Name>はカテゴリ名
・<Ancestors>は、現在のカテゴリより「階層が上」のカテゴリ
という感じ。

実際にXMLをパースするときはおそらくDOMやらを使うんだろうけど、携帯サイトでそんなことやったら結構な負荷だし自分で実装するのもめんどくさい。
そこで、正規表現で切り出そうと考えた。

上のXMLをよく見ると、「</BrowseNode>」がはじめて出てくるのが「一番上の階層の閉じタグ」、ということに気づいた。
であれば、
「/<BrowseNode>(.*?)</BrowseNode>/」
というような正規表現パターンで「階層の上半分を取ってこれる(上の例の青い字の部分を取り出せる)。

ここからもう一度正規表現で<BrowseNodeId>と<Name>を切り出せばOK。
結構簡単なことだったんだなぁ、と思ったw


---注意----
BrowseNodeには「Children」という商品が属してるカテゴリよりも下位の階層のカテゴリも返してくれる。
今回はそこまで必要じゃなかったので割愛。

2011-02-11

【シェルスクリプト】Linuxサーバで自動的にFTPダウンロードする方法

Linuxサーバで自動的にFTPを実行するようなシェルスクリプト作ってみた。
簡単な定型処理だけならPerlとかよりシェルスクリプトのほうが手軽でいいよね。

今回のスクリプトの仕様は以下の通り。

◆仕様
・bashのシェルスクリプトで作成する
・何らかのログファイルを毎日ダウンロードする(今回はWebアクセスログ的なもの)
・ログファイル名には日付が入っており、実行する日の前日分のファイルをダウンロードする
・ログファイルをダウンロードしたら、リモートホストのファイルは削除する

というような感じにした。
各変数の説明は以下の通り。

◆変数の説明
・CURRENT_DIR
  実行時のディレクトリ。
・DEST_DIR
  ダウンロードしたファイルを保存するディレクトリ。必要ないなら指定しなくても良い。
・DATE_OF_YESTERDAY
  「YYYY-MM-DD」というフォーマットの「前日分日付」
・REMOTE_HOST
  接続するホスト名。IPアドレスも可能
・REMOTE_USER
  リモートホストのユーザ名
・REMOTE_PASS
  リモートホストのパスワード
・REMOTE_PATH
  リモートホストの対象ファイルが置いてあるディレクトリ。
・REMOTE_FILE_YESTERDAY
  リモートホストの「前日分」のログファイル名

◆使い方
以下のスクリプトをEUCエンコードと改行コード「LF」で保存し、パーミッションは「755」または「705」で保存して、クーロンに実行したい時間を指定して登録すればOK。

Gumroadで販売中(200円) → https://gumroad.com/l/ptKl

以上。

2011-01-20

【MySQL】バックアップ&レストアツール作ってみた。

ちょっと前からブログのネタにしてたMySQLだけど、結局MySQL附属の「mysqldump」とかではうまくいかなかったんだよね。
で、しょうがないからMySQLのバックアップとレストアツールを作ってみたよ。

まぁ、大規模なデータベースのバックアップはちょっとムリかもしれないけど、数百MBくらいのテーブルなら何とかなるかもしれない。

●仕様
・最大1つのデータベースのみバックアップ・レストアできる。
・必ずしも元のデータに復元できないことに注意すること!!
※複数のデータベースをバックアップしたいときは、このプログラムを複数の場所に入れればOK。

●バックアップの処理内容
全てのテーブル名を取得し、データを全件SELECTしてbase64でエンコードし保存するだけ。
テーブル1つに対しファイル2つが出来る。「CREATE文が入るファイル」と「データが入るファイル」となる。
たとえば「hoge」というテーブルがあるのであれば、「hoge_create.dat」と「hoge_data.dat」というファイルが出来る。
各レコードを各1行に収めるために改行文字を「¥r¥n」などに変換していることに注意。

●レストア処理の内容
ファイル名から「CREATE文」か「データ」かを区別しCREATE文から処理を始める。フラグを設定すれば「DROP TABLE IF EXISTS文」も入れられる。
データはbase64デコードしINSERT文を作って入れるだけ。データ項目に「'(シングルクォート)」が入ってる場合は二重化して回避。

●使い方
▲ダウンロードする
http://www.tagajo.tv/backuptool_ver01.zip
↑からツールをダウンロードして、展開する。このときディレクトリ構造をいじらないこと。


▲設定を環境に合わせる
「ダウンロードする」で展開したファイルの中に「config.php」というファイルがあるので、その中身を変更する。
具体的には以下の部分のみを変更すればOK。

---引用開始---
 // バックアップするデータベースの設定
 define( 'DATABASE_HOST',  'xxxxxxxxxxxxx' );
 define( 'DATABASE_USER',  'xxxxxxx' );
 define( 'DATABASE_PASS',  'xxxxxxx' );
 define( 'DATABASE_NAME',  'xxxxxxx' );

 // レストアするデータベースの設定
 define( 'RESTORE_HOST',   'xxxxxxxxxxxxx' );
 define( 'RESTORE_USER',   'xxxxxxx' );
 define( 'RESTORE_PASS',   'xxxxxxx' );
 define( 'RESTORE_NAME',   'xxxxxxx' );

 ~中略~

 // MySQLのクライアント文字コード(お使いのデータ本体の文字コードへ変更してください)
 // SET NAMES *** に使用されます。
 define( 'DATA_CHARACTER_CODE',  'sjis' );
---引用終了---

※注意
「DATABASE_XXXX」はバックアップするデータベースの設定
「RESTORE_XXXX」はレストアするデータベースの設定
なのを間違わないこと。

▲バックアップするサーバへアップロードする
設定を環境に合わせたら、バックアップ対象のデータベースがあるサーバへアップロードする。
アップロードする場所はどこでもOKだけど、Webで公開されているディレクトリはやめること。
セキュリティなんぞこのプログラムは考慮してないので。
アップロードするときも、ディレクトリ構造をいじらないこと。
アップロード先の「dat」ディレクトリには「700」等の書き込みできるパーミッションを設定しておくこと。

▲実行方法
PHPで記述されているのでPHPのインタプリタで実行する。
以下、PHPが「/usr/local/bin/」の下にあるとする。

・バックアップ実行コマンド
$/usr/local/bin/php -f /アップロードしたディレクトリ/backup.php

・レストア実行コマンド
$/usr/local/bin/php -f /アップロードしたディレクトリ/restore.php

以上。

2011-01-11

【MySQL】異なるサーバ間でのデータ移行方法について

Kagoyaのサーバで運営していたサイトを、さくらインターネットのレンタルサーバへ移動したときの作業メモです。
MySQLを使用してサイトを運営しているのですが、基本的にMySQL附属のダンプツールは文字化けなどの問題がたくさんあってあまり使えないですね。
さらにKagoyaサーバのmysqldumpは文字コードの指定を受け付けないようになってるみたいだし。

しょうがないので自分でダンプツールとレストアツールを作成し移行してみることにしました。

---
○サイトの仕様
・データベース数:1つ
・データベース容量:100MB程度
・レコード総数:26万件程度

○テーブルの移行方法
・テーブル自体の構造はphpMyAdminで構造だけ取り出し、移行する

○ダンプツールの仕様
・テーブル1つごとにダンプファイル1つで、テーブル名をファイル名とする
・ファイル内容は1行ごとに1レコード
・文字コードはデータに合わせる
・1行ごとに1レコードなので改行文字は「\r」や「\n」と表記し無理やり1行に収める

○レストアツールの仕様
・ダンプファイルから1行を読み出しINSERT文を作成しテーブルへINSERTする
・文字化けしないように、PHPの「mysql_escape_string関数」をかませる
・mysql_escape_stringを使用すると、ダンプツールで変換した「\n」が「\\n」と変換されてしまうので「\n」へ戻す
---

といった感じにしました。

---
○問題点と回避方法
・ダンプツールで大容量のテーブルをファイルに吐き出そうとするとメモリが足りない、とPHPに怒られる
→mysql_query関数を使用していたため、メモリに結果をバッファしようとして大量のメモリを使用していたことが分かり、メモリを極力使用しない「mysql_unbufferd_query関数」を使用することで回避。

・CSVでデータを吐き出すとデータ内のカンマなどで不整合がおきメンドイ
→「mysql_fetch_assoc関数」を使用し、「連想配列」で結果を返してもらうことにする。それを「serialize関数」で直列化して保存しておけば、読み出し側で「unserialize関数」で元に戻せる。
---

これでデータが全てダンプファイルに落とされたので、移動先のサーバへ持っていってレストアツールでレストアすれば作業完了。文字化けもしないはず。
あと、オマケとしてツールのソースコードを以下に掲載しておきますね。



○ダンプツールのソース
---
  function nl_2_char( &$item, $key )
  {
    $item = preg_replace( '/\r\n/i', '\r\n', $item );
    $item = preg_replace( '/\r/i', '\r', $item );
    $item = preg_replace( '/\n/i', '\n', $item );
  }
 
  define( 'DATABASE_HOST',    'ホスト名' );
  define( 'DATABASE_USER',    'ユーザ名' );
  define( 'DATABASE_PASS',    'パスワード' );
  define( 'DATABASE_NAME',    'データベース名' );
 
  $con = @mysql_connect( DATABASE_HOST, DATABASE_USER, DATABASE_PASS, false );
  mysql_select_db( DATABASE_NAME );
 
  $tables = array( テーブル名の配列 );
 
  foreach( $tables AS $tab    le ) {
    $file_name = dirname( __FILE__ ) . "/dat/" . $table . '.dat';
    @unlink( $file_name );
   
    $strSql  = "SELECT * FROM " . $table;
    $resource = mysql_unbuffered_query( $strSql, $con );
    if( !$resource ) {
      print( "ERROR!" );
      exit();
    }
    $table_data = array();
   
    print( $table . " START!\n " );
   
    while( $row = mysql_fetch_assoc( $resource ) ) {
      array_walk( $row, 'nl_2_char' );
      $buff = serialize( $row ) . "\n";
      file_put_contents( $file_name, $buff, FILE_APPEND );
    }
    unset( $resource );
   
    print( $table . " END!\n " );
  }
?>
---

○レストアツールのソース
---
  define( 'DATA_PATH',    dirname( __FILE__ ) . '/dat/' );
 
  define( 'DATABASE_HOST',    'ホスト名' );
  define( 'DATABASE_USER',    'ユーザ名' );
  define( 'DATABASE_PASS',    'パスワード' );
  define( 'DATABASE_NAME',    'データベース名' );

  // ファイルネーム取得
  $file_names = array();
  $d = dir( DATA_PATH );
  while ( false !== ($entry = $d->read()) ) {
    if( preg_match( '/[a-z0-9\_]+/', $entry ) ) {
      $file_names[] = $entry;
    }
  }
  $d->close();
 
  $con = @mysql_connect( DATABASE_HOST, DATABASE_USER, DATABASE_PASS, false );
  mysql_select_db( DATABASE_NAME );
  mysql_query( 'SET NAMES sjis' );    // データベースの文字コードにあわせて変更すること!
 
  foreach( $file_names AS $file_name ) {
    $table_name = basename( $file_name, '.dat' );
   
    // $rows = file( DATA_PATH . $file_name );
    $fp = fopen( DATA_PATH . $file_name, 'r' );
    if( !$fp ) {
      die( "DONT OPEN!\n" );
    }
   
    while( !feof( $fp ) ) {
      $row = fgets( $fp );
      if( trim( $row ) == "" ) {
        continue;
      }
      $fields = unserialize( trim( $row ) );
     
      $srcSql  = "INSERT INTO %s (%s) VALUES (%s)";
      $field_names = array();
      $field_values = array();
      foreach( $fields AS $field_name => $value ) {
        $field_names[]  = $field_name;
        $value = mysql_escape_string( $value );
        $value = str_replace( '\n', 'n', $value );
        $value = str_replace( '\r', 'r', $value );
        $field_values[]  = sprintf( ' %s ', $value );
      }
      $field_list = implode( ", ", $field_names );
      $value_list = sprintf( "'%s'", implode( "', '", $field_values ) );
      $strSql = sprintf( $srcSql, $table_name, $field_list, $value_list );
     
      $ret = @mysql_query( $strSql, $con );
      if( !$ret ) {
        print( $strSql . "\n" );
        print( mysql_error() );
        exit();
      }
    }
  }
?>
---

ZenBack

WebMoney ぷちカンパ