ほんじゃらねっと

ダイエット中プログラマのブログ

プログラムテスト用の組み合わせパターンデータをササッと作るためのテンプレ【Python/Clojure】

f:id:piro_suke:20160729002821j:plain

他の誰かが作成した検索プログラムやストアドファンクションを

修正させられる時に、

検索対象のテストデータとか

プログラムに渡す検索パラメータのテスト用データを

必要なパターン分作成してテストするのだけど、

抜け漏れのないテストデータを簡単に生成できないものかと調べていたら、

PythonにもClojureにも組み合わせリストを作成してくれるライブラリがあった。

続きを読む

Pythonのジェネレータを使って大容量ファイルを分割する

過去に下記のような、ファイルを複数に分割する方法について記事を書いた:

blog.honjala.net

この記事に書いたコードをもう少しエレガントに書けないものか、

と方法を調べていて、

Pythonのジェネレータが使えそうだったので色々試してみた。

続きを読む

PythonでSSHトンネリングしてデータベースアクセス

f:id:piro_suke:20160625013046j:plain

稼働中のWebアプリのデータベースに定期的にアクセスして

データの状態をチェックしたい、でもデータベースには

外部から直アクセスできないようになっているので、

SSHトンネル経由でないとアクセスできない、という場合。

A5:SQLなりpgAdminなり最近のDBアクセスツールは

大抵SSH経由でのアクセスに対応しているので、

こういったツールを使えばDBに手動アクセスすることができる。

A5:SQL Mk-2 - フリーの汎用SQL開発ツール/ER図ツール

自分で書いたプログラムからSSH経由でDBにアクセスしたい場合は、

WindowsならPutty(SSHクライアントソフト)が

SSHトンネリング機能を持っているので、PuttyなりCUIツールの

plinkなりを立ち上げておくことで割と簡単に実現できる。

qiita.com

今回紹介するのは、

Windows以外のクライアントからアクセスする場合だったり、

WindowsだけどPutty立ち上げるのめんどくさい、という場合に

PythonプログラムでSSHトンネリングを実現する方法。

続きを読む

SQL Serverから出力したrptファイルを使いやすいようにcsvファイルに変換する【Python】

f:id:piro_suke:20160622005308j:plain

SQL Serverからエクスポートしたrpt形式ファイルを渡され、

CSVに変換してくれと依頼される。

「あれ、SQL Server Management Studioに

直接CSV出力する機能ありませんでしたっけ?」

と確認しても「しらん。やれ。」と言われる。

そんなよくある状況で助けてくれるrpt2csv変換スクリプトを作成した。

続きを読む

ボケてきたのかタイムカードの打刻を忘れてしまうので、打刻漏れ防止用チェックプログラムを作成した

f:id:piro_suke:20160620020919j:plain

毎朝出社時に社員カードで入室し、

入室したのと同じカードで出勤打刻をするのだが、

入って数歩のところにタイムレコーダーが置いてあるにも関わらず、

打刻が漏れることがある。

どうやら、

雨の日に途中で傘を置いたり(傘立てはドアとタイムレコーダーの間にある)

誰かと会って話しながら入室したりすると

ルーチンが壊れて打刻を忘れてしまうことがあるようだ。

どれだけ変化に弱いルーチンなんだ、という話だが、

ともあれ打刻が漏れると手続きが面倒なので、

自動チェックの仕組みを考えた。

続きを読む

iPythonでコマンド履歴ログ出力を自動化する

たまにはPythonを使っておこう、

ということでここ数日シェル環境としてiPythonを使っている。

Jupyter and the future of IPython — IPython

ほぼPython言語としての機能は使っていないのだけど、

ちょっとした計算をしたりするにはなかなか便利だ。

せっかく書いた処理を使い捨てにするのはもったいないので、

入力履歴をファイルに自動ログしてくれるスタートアップスクリプトを作成した。

ネタ元は下記のstackoverflowの回答から:

stackoverflow.com

iPythonは下記のスタートアップフォルダに

pythonファイルを作成しておくと起動時に実行してくれる:

~/.ipython/profile_default/startup

そこに上のstackoverflow回答に書いてあるとおり、

下記のようなスクリプトを保存しておくと

指定したフォルダにiPython起動時の日時をファイル名としてログを出力してくれる。

01-log-everything.py

from time import strftime
import os.path

ip = get_ipython()

ldir = <ログ保存先フォルダ>
fname = strftime('%Y-%m-%d-%H-%M-%S') + ".py"
filename = os.path.join(ldir, fname)

ip.run_line_magic('logstart', '-o %s append' % filename)

便利。

IPythonデータサイエンスクックブック ―対話型コンピューティングと可視化のためのレシピ集

IPythonデータサイエンスクックブック ―対話型コンピューティングと可視化のためのレシピ集

Pythonでオブジェクトから階層ごとに属性があるかチェックせずに値を取得するための関数

AmazonAPIを触っていて、レスポンスとして返ってきた(XMLから生成された)オブジェクトから値を取得する際にいちいちhasattrでチェックするのが面倒だったので、そのパスがなければデフォルト値を返す関数を作った。

見つけられなかったけど、標準ライブラリにありそう...。

hasattrで属性があるかチェックして、 あったら次の階層に進み、なかったらデフォルト値を返す。

オブジェクトの属性が

obj.__dict__[属性名]

で取得できることが分かったのは収穫だった。(Python2.7)

下記のような形でAmazonAPIからのレスポンスを処理してみたらうまく動いた。

amazon_test.py

import bottlenose
from lxml import objectify

amazon = bottlenose.Amazon(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY, Region='JP')

res = amazon.ItemSearch(
        Keywords=u'ダイエット'
        , SearchIndex='Books'
        , ItemPage='1'
        , ResponseGroup='Medium'
        , AssociateTag=AWS_ASSOCIATE_TAG
)

oot = objectify.fromstring(res)
for item in root.Items.Item:
    title = get_obj_attr(item, 'ItemAttributes.Title')
    author = get_obj_attr(item, 'ItemAttributes.Author')
    rank = get_obj_attr(item, 'SalesRank', 0)
    print '%s: %s, rank:%s, author:%s' % (item.ASIN, title, rank, author)

ちゃちゃっとAPIを試したい時なんかに使ってみよう。

Pythonプロフェッショナルプログラミング 第2版

Pythonプロフェッショナルプログラミング 第2版

PythonでExcelの表からINSERT用SQLを生成して初期データ作成を楽にする

職場はメイン言語がJavaなので、プロジェクトでPythonを使う機会は ないのですが、キャプチャを撮ったりExcelを操作したりする作業を 「こっそり自動化」するスクリプトPythonで書いたりしています。

最近はSE作業で何かとExcelを使うことが多いので、 Excel作業の自動化をいろいろと模索しています。

PythonExcel操作できるライブラリはいくつかあって、 xlwingsExcelPythonなんかが話題になっていたので 調べてみたのですが、後々他のメンバーに作成した ファイルを共有したりすることを考えると、 Excelと切り離して処理が書ける方が良さそうです。 (xlwingsはVBAPythonを呼び出す必要があり、ExcelPythonはアドオンを入れる必要がある)

結局、オーソドックスなxlrdxlwtを使っています。

今日は以前から面倒だと思っていた、 初期データ作成用INSERT文を作成するスクリプト、 というか関数を作りました。 Windows環境で使うことを想定しています。

下記のような、1行目がテーブルのカラム名で、 2行目以降がデータになっているシートを読み込んで、 行ごとにINSERT文を作成して別ファイルに出力します。

ID NAME ADDRESS
1 山田 大阪府
2 岡田 東京都

出力先ファイルやテーブル名は引数で指定します。 複数のシートを一括処理可能です。

使用例:

output_insert_sqls('テストデータ.xlsx', 'C:/Users/test/Desktop/insert_data.sql', 
    [   
        '会員一覧' 
        ,'商品一覧' 
    ],  
    [   
        'users'
        ,'products'
    ]   
)

他にも、CREATE TABLE文やDELETE文を生成する関数を追加すれば、 色々と作業を楽にしていけそうです。

セルを処理している部分は文字列とfloatしか考慮しておらず、 荒めのコードになっているので、またブラッシュアップしていきます。

unicode_literalsは今回初めて使い、どういう原理か 全く分かっていませんが、とりあえずこれをインポートすることで Windowsのパスが正しく認識できるようになりました。

迷惑をかけないExcel

迷惑をかけないExcel

Python+ScrapyでWeb上のリストからデータを抽出する

昨日の続きでScrapyをさわる。

ちなみに、昨日Linux環境にインストールする時は苦労したが、 今日Windows環境にpipでScrapyをインストールしてみたら、 何ひとつひっかからずにインストールが完了した。

ともあれ、ようやくScrapyをインストールできたので、 試しにWebサイトのページからデータを抽出してみる。

毎週楽しみにしているトラベリックスの番組内容一覧を取得してみた。 ここは履歴のみ掲載されているけど、同じようなスクリプトを使って 次週の放送予定を取得する、みたいなこともできそう。

放送回、放送日、タイトル、詳細ページのURLを取得して出力する。

travelix_spider.py

import scrapy

class TravelixSpider(scrapy.Spider):
    name = 'travelix_spider'
    start_urls = ['http://www.bs4.jp/document/travelix3/onair/index.html']

    def parse(self, response):
        for table in response.xpath('//table'):
            row = table.xpath('tr/td')
            if len(row) == 4:
                num = row[0].xpath('text()').extract_first()
                date = row[1].xpath('text()').extract_first()
                title_link = row[2].xpath('a')
                url = title_link.xpath('@href').extract_first()
                title = title_link.xpath('text()').extract_first()
                print 'num: %s, date: %s, url: %s, title: %s' % (num, date, url, title,)

実行は、scrapyコマンドを使用する。

scrapy runspider travelix_spider.py

ドキュメントをちゃんと読んだらもう少し洗練された コードにできるかな?

スクレイピングを使いこなせば APIで提供されていないデータや、HTMLベースでしか存在しないデータを データベース化する役に立つので、データ収集・抽出ツールとして 押さえておく価値はありそうです。

実践 Webスクレイピング&クローリング-オープンデータ時代の収集・整形テクニック

実践 Webスクレイピング&クローリング-オープンデータ時代の収集・整形テクニック

CentOSのPython2.7環境にScrapyをインストールするまで

ひとつPythonでWebスクレイピングでもやってみようかとScrapyVPS環境に入れてみたところ、インストールするだけで手間取ってしまった。

環境はPython2.7.3で、インストールしてから特に何も設定していない状態。

本サイトに書いてある通り、まずは

pip install scrapy

を実行してみた。

するとUnicodeDecodeErrorが発生する。 これはよくあるPythonの初期設定の問題なので、 /usr/local/lib/python2.7/site-packages/sitecustomize.py を作成して、下記の内容を書いて保存しておく。

import sys
sys.setdefaultencoding('utf-8')

再度pipを実行すると、またエラー。どうやらlxmlがはいっていないらしい。

pip install lxml

エラー。必要なライブラリが足りないらしい。

# yum install libxml2* libxslt*
pip install lxml

成功。でもscrapyのインストールは失敗。 cffiが見つからない、というエラーが出ている。

sudo yum install libffi*
pip install scrapy

これでようやくインストール成功。 もう寝る。続きは明日やる。

パーフェクトPython

パーフェクトPython

PythonとPandasで作業履歴から案件別作業別のピボットテーブルを作成する

作業履歴が入ったデータベーステーブルから、 案件情報を行に、作業内容(「設計」とか「PG」とか)を列に、 案件別作業内容別合計時間を値として持つピボットテーブルを作成して Excelで確認する必要があったので、以前から興味のあったPandasを 使ってスクリプトを書いてみた。

作業履歴テーブル(work_histories)は下記のような構成とする:

案件名 customer_name
作業名 type_name
作業時間数 total_work_time

genka_checker.py

import psycopg2
import pandas as pd
import pandas.io.sql as psql

OUTPUT_CSV_PATH = '<CSVファイル出力パス>'

conn = psycopg2.connect(
    host='<DBホスト名>',
    port='<DBポート番号>',
    database='<DB名>',
    user='<DBユーザー名>',
    password='<DBパスワード>'
)

sql = 'select customer_name, type_name, sum(total_work_time) as sum_time from work_histories group by customer_name, type_name order by customer_name'

df = psql.read_sql(sql, conn)
df.groupby('type_name').agg(np.sum)
pd.pivot_table(df, values='sum_time', index=['customer_name'], columns=['type_name']).fillna(0).to_csv(OUTPUT_CSV_PATH, encoding='cp932')

conn.close()

初めてPandasを使ったけど、色々便利な機能があって勉強する価値がありそう。

次は合計作業時間ではなく、案件ごとの作業の割合を出したい。 正しく見積もるためのヒントが見えてきそう。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

指定したフォルダ内のファイルのファイル名から特定のパターンの文字列を一括削除する

フォルダを指定してその中のファイルのファイル名の一部を削除するちょっとしたスクリプト。子フォルダの再帰処理はしない。
001_(hoge).txt、002_(fuga).txtなどのファイルを001_hoge.txt、002_fuga.txtに一括変換したい時なんかに使えるよう、正規表現でパターンを指定できるようにした。

使い方

python exclude_file_name.py フォルダパス 置換パターン
python exclude_file_name.py target_folder [¥(¥)]


exclude_file_name.py

!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import os.path
import re
root = sys.argv[1]
ex_pattern = sys.argv[2]
def main():
files = os.listdir(root)
for file_name in files:
new_file_name = re.sub(ex_pattern, "", file_name)
src = os.path.join(root, file_name)
dest = os.path.join(root, new_file_name)
print src + " => " + dest
os.rename(src, dest)
if __name__ == "__main__":
main()