2016年9月14日水曜日

Python | 画像ファイルのExif情報を取り出す

普段デジカメで写真を撮ることが多いので、遠くない将来、撮りためた画像のExif情報から自分の傾向分析なんかをすることになる気がしています。

その時のために、画像ファイルのExif情報をPythonで書いたスクリプトを使って取得する方法を調べてみました。

ネットで検索すると、PIL(Python Image Library)というモジュールを使っている例が多いようなのですが、使い方がいまいちピンとこなかったので、別の方法として、ExifReadを使ってみることにしました。

ExifReadをDLする

ExifReadはこちらからDLできます。


モジュールを適切な場所に置く

解凍したモジュール一式を僕は"C:\Python27\Lib"の中に置きました。

スクリプトを書く

下を参考にしてみてください。
僕はこのサイトに教えてもらいました。

---
#!/usr/bin/env python
#-*- coding:utf-8 -*-

# 目的
# 画像ファイルからExif情報を取り出す

# 使い方
# 1.このスクリプトファイルを画像データのあるフォルダに置く
# 2.必要に応じて拡張子の指定部分を書き換える
# 3.実行する

from exif_py_develop import exifread
import glob

photos = glob.glob('*.JPG')

for f in photos:
    fname = open(f,'rb')
    tags = exifread.process_file(fname)

    for tag in tags:
        if tag not in ('JPEGThumbnail', 'TIFFThumbnail', 'Filename', 'EXIF MakerNote'):
            print "Key: %s, value %s" % (tag, tags[tag])
---

実行結果

スクリプトファイルは画像ファイルと同じフォルダに置いて実行します。
Exifにはたくさんの情報が入っていますが、例えば撮影日時であれば

Key: EXIF DateTimeDigitized, value 2016:08:24 16:44:22

2016年8月24日16時44分22秒に撮影された、と出力されます。

注意点やメモ

  • DL,解凍したモジュールを最初にインポートしています。
  • フォルダ内の全画像ファイル(拡張子JPG)を取得するのにglob関数を使いましたので、globモジュールもインポートしています。このとき、拡張子の指定は文字列で、大文字小文字も区別されます。

2016年7月7日木曜日

Python | for文での繰り返しを降順(逆順)で回す

for文では通常、指定範囲の中でインデックスの値の小さい方から大きい方へ向かって処理が進みますが、この方法で問題となるのが、スプレッドシートなどで条件に合う行を削除する場合です。

スプレッドシートで1行削除すると行の位置が1つ上にずれてしまい、本来削除したい行とはずれが生じてしまいます。

それを解決するのが、スプレッドシートの下のほうの行から上に向かって行の削除を実行していく方法になります。この方法のスクリプトはこちらを参考にしてください。PythonではなくGoogle Apps Scriptで書いたものですが、イメージは伝わるかと思います。

ここで問題となるのが、Google Apps Script(JavaScript)のfor文で"--i"、つまり i = i -1 で表現している、降順でのループの回し方を、Pythonの文法でどう表現するのか、ということです。

結論としては、

for i in range(1, 10):

のような書き方のforループを逆順で回す場合、

for i in reversed(range(1, 10)):

とします。

2016年6月10日金曜日

Python | 複数の画像ファイルをWordの表に貼りつける方法

Microsoft Wordの表の1セルに1コマずつ、複数(多数)の画像ファイルを貼りつける場面があり、手動ではかなりの時間がかかったため、効率化をのためのメモを残しておきます。

前提として、全画像ファイルが同一フォルダに置かれているものとします。また、画像ファイルの入っているフォルダには、画像ファイル以外にはPythonスクリプトファイルのみ置かれているものとします。

#---
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import win32com.client as win32

def insertImagesIntoWord(arg):

    wordApp = win32.gencache.EnsureDispatch('Word.Application') #create a word application object
    wordApp.Visible = 1 # Wordを表示します
    doc = wordApp.Documents.Add() # create a new application

    doc.PageSetup.RightMargin = 15 # Wordのページの右側余白を指定します
    doc.PageSetup.LeftMargin = 15 # Wordのページの左側余白を指定します
    doc.PageSetup.Orientation = win32.constants.wdOrientLandscape
    # A4 サイズ: 595x842
    doc.PageSetup.PageWidth = 595
    doc.PageSetup.PageHeight = 842

    total_column = 3 # Wordの表の列数を指定します
    total_row = 17 # Wordの表の行数を指定します
    rng = doc.Range(0,0)
    rng.ParagraphFormat.Alignment = win32.constants.wdAlignParagraphCenter
    table = doc.Tables.Add(rng,total_row, total_column)
    table.Borders.Enable = False
    if total_column > 1:
   table.Columns.DistributeWidth()

    frame_max_width= 167 # the maximum width of a picture
    frame_max_height= 125 # the maximum height of a picture

    for index, filename in enumerate(filenames):

        if filename[len(filename)-4: len(filename)].upper() == 'JPEG':

# 各画像を貼りつける位置を計算します
cell_column= index % total_column + 1
cell_row = index / total_column + 1

# セルの形式を整えます
cell_range= table.Cell(cell_row, cell_column).Range
cell_range.ParagraphFormat.LineSpacingRule = win32.constants.wdLineSpaceSingle
cell_range.ParagraphFormat.SpaceBefore = 0
cell_range.ParagraphFormat.SpaceAfter = 3

# ここで画像を貼りつけています
current_pic = cell_range.InlineShapes.AddPicture(os.path.join(os.path.abspath("."), filename))
width, height = (frame_max_height*frame_max_width/frame_max_height, frame_max_height)

# 画像の大きさを表のセルサイズに合わせる場合はこちらも追加します
#current_pic.Height= height
#current_pic.Width= width

# 各セルに対応する画像のファイル名を入力します
table.Cell(cell_row, cell_column).Range.InsertAfter("\n"+filename)


if __name__ == "__main__":

    currentDir = os.getcwd() # スクリプト実行時のカレントディレクトリのパスを取得します
    filenames = os.listdir(currentDir)
    insertImagesIntoWord(filenames)
#---

こちらがベースです。
ファイルがJPG形式かどうかを確認する部分の書き方がくどく感じますが、これで当初の目的はほぼ果たせました。


2016/07/15追記
対象のフォルダ内に画像ファイルとスクリプトファイル以外のファイルが存在すると、画像の貼り付けが表の左上からではなく、ずれてしまいます。

2015年11月10日火曜日

Windowsダイアログを使って編集するファイルを指定する方法2 | Python

テキストファイルの指定行頭に"$"を追記し、指定外の行はオリジナルのまま別ファイルに書き出すことを考えます。"$"をつけた行は読み込まれなくなるものとして、関数名はcommnetOutとしています。

---以下スクリプト---

import win32ui
import os.path

# Windowsダイアログを作ります
o = win32ui.CreateFileDialog( 1, ".inc", "", 0, "Text Files (*.inc)|*.inc|All Files (*.*)|*.*|")
o.DoModal()

# ダイアログで指定したファイルのパスを取得して変数inFileに割り当てます
inFile = o.GetPathName()

def commentOut():

    try:
        # ファイルの保存先とファイル名を指定するダイアログを作ります
        o = win32ui.CreateFileDialog( 1, ".inc", "", 0, "Text Files (*.inc)|*.inc|All Files (*.*)|*.*|")
        o.DoModal()
        # ファイル保存先のパスを取得して変数outFileに割り当てます
        outFile = o.GetPathName()

        fin = open(inFile,"r")
        line_list = fin.readlines()

        fout = open(outFile,"w")

        #コメントアウトしたいスタート行番号を指定します
        start_line = 646189
         #コメントアウトしたい最終行番号を指定します
        end_line = 652848

        #読み込んだテキストファイル全行が入ったリストの最初から最後まで繰り返します
        for index,line in enumerate(line_list):
            if start_line-2 < index < end_line:
                fout.write("$" + line)
            else:
                fout.write(line)

        fout.close()
        fin.close()

    except:
        pass

    finally:
        pass

if __name__ == "__main__":
    commentOut()

---以上スクリプト---

以前の投稿で書いたWindowsダイアログの作り方とは別の書き方となっていますが、操作結果は同じようなものになります。

2015年11月6日金曜日

Windowsダイアログを使って編集するファイルを指定する方法1 | Python

Excelファイルでのデータ処理をPythonで記述したスクリプトを使って行う場合を想定します。

---
import win32com.client

xlApp = win32com.client.Dispatch("Excel.Application")
xlApp.Visible = 1

xlApp.Workbooks.open(r"C:\training\test.xlsx")
xlSheet = xlApp.Workbooks(1).Sheets(1)

xlCell = xlSheet.Cells(2,1)
xlCell.Value = "aaa"
---

これを実行すると新規にExcelが起動し、セルA2にaaaが入力されます。

次に、Windowsのダイアログから開きたいファイルを指定する方法です。

---
import win32com.client
import win32ui #ダイアログを開くために追記します

o = win32ui.CreateFileDialog( 1, ".txt", "default.txt", 0, "Text Files (*.txt)|*.txt|All Files (*.*)|*.*|")
o.DoModal() #( )内はダイアログ上の表示を規定しています

xlApp = win32com.client.Dispatch("Excel.Application")
xlApp.Visible = 1

xlFile = o.GetPathName() #ダイアログで指定したファイルのパスを変数xlFileに入れています

xlApp.Workbooks.open(xlFile)
xlSheet = xlApp.Workbooks(1).Sheets(1)

# A2セルにaaaと書き込む
xlCell = xlSheet.Cells(2,1)
xlCell.Value = "aaa"
---

これでダイアログから開きたいファイルを指定することができます。

ファイル名は隠しています


しかし、問題もあります。
それは、ダイアログでキャンセルボタンが押された時の処理が書かれていないことです。
これについての対応方法は別の投稿で書きたいと思います。

2015年10月27日火曜日

Python | リストのインデックスと値を同時に取得する

テキストファイルの指定行をコメントアウトする作業をPythonで自動化することを考えます。
ここでは、コメントアウトする方法として行頭に"$"を付けることにします。

処理の流れを書き出すと、


  1. コメントアウトしたい行(スタート行とエンド行)を指定しておく
  2. テキストファイルの全行をリストに格納する
  3. リストに格納した行の内容を順番に書き出す
  4. 1.で指定した行に達したら、行頭に"$"を付けて書き出す作業をエンド行まで繰り返す
  5. エンド行より後の行からはリストに格納した内容のまま書き出す
---
    start_line = 9 #コメントアウトしたいスタート行番号
    end_line = 641739 #コメントアウトしたい最終行番号

    for index,line in enumerate(line_list):
        if start_line-2 < index < end_line:
            savefile.write("$" + line)
        else:
            savefile.write(line)
---

この時、行番号とリストのインデックスが対応するため、enumerate()関数でリストの中身とインデックスを同時に取得しています。

2015年8月21日金曜日

オンラインでGoogle Apps Scriptを学ぶ方法 | schoo(スクー)

オンライン学習サービスの1つであるschoo(スクー)をご存じでしょうか?

様々な分野の講義・講座の動画が配信されていて、生放送と録画があります。
生放送では、授業配信サイトを開いたウェブブラウザ上で講師への質問や生徒同士のやり取りをリアルタイムにチャットのようにすることができるのが特徴で、逆に講師側から生徒への質問もあります。

様々な授業の中に、初心者向けGoogle Apps Scriptによるプログラミング講座があります。
現在録画で受講することができるものとして、例えば、

Google Apps Script 入門 -基礎知識編-
Google Apps Script 入門 -Google Spread Sheet編-

があります。

先日はForm編の生放送がありましたので、後日録画配信されるものと思います。

録画版は内容に応じて無料のものと有料(チケット制)のものがあるため、過去の全ての授業を受けられるわけではありませんが、SNSやメールを通じて招待した友人が受講登録することでチケットがもらえたりしますので、興味のある方は一度サイトを覗いてみてはいかがでしょうか。

僕は通勤で片道1時間程度、電車に乗っているため、通勤時間中に受講することでかなり有効に時間を使えるようになったと感じています。

2015年6月3日水曜日

フォーム回答内容をメールで通知する | Google Apps Script

※投稿後に動作確認をしたところ不具合が見つかったため、スクリプトを修正しました。

Googleのサービスの1つにフォームがあります。

アンケートを作成し、回答結果をスプレッドシートにまとめることができて便利なのですが、回答がどれくらい集まっているかを確認するのに、毎回スプレッドシートを開かなければならないのは面倒です。私の場合、最も頻繁に利用するメールで回答状況をざっくりとでも把握したいと考えます。そこで、回答があったときに、その内容を指定したメールアドレスに送るスクリプトを用意しておきます。

---以下スクリプト---
function sendAnswer() {

    var ss = SpreadsheetApp.getActiveSheet();
    var dataRange = ss.getDataRange();
    var lastRow = dataRange.getLastRow();
 
    var date = ss.getRange("A"+lastRow).getValue();
    var name = ss.getRange("B"+lastRow).getValue();
    var answer = ss.getRange("C"+lastRow).getValue();

    var subject = "フォームの回答がありました"

    var mailBody = date + "\n"
                       + name + "\n"
                       + answer;
    var mailTo = "メールアドレス";

    GmailApp.sendEmail(mailTo,subject,mailBody);
}
---以上スクリプト---

フォームでの最新の回答は、前の回答の1つ下の行に入力されます。つまり、最新の回答内容を取得するにはデータ最下端行の各セルの値を取得すればよいことになります。

最終行A列:最新回答送信日時(タイムスタンプ)
最終行B列:本例では回答者の名前
最終行C列:回答内容
(アンケートの設問ごとに回答が入力される列が分かれます)

あとは、Google Apps Scriptでのメール送信ルールに従い、変数subject,mailTo,mailBodyで指定したメール件名と宛先、本文を使って記述します。

最後に大事なことが1つあります。

回答があった時にタイムリーにメール送信してくれると助かりますよね。
それはトリガーで実現します。

フォームから回答が送信されたときにsendAnswer()関数を起動させるトリガーを設定すれば、目的達成です。

2015年1月31日土曜日

Microsoft Excelのソルバー機能の用途 | ポートフォリオリバランス計算

Excelの機能の1つにソルバーがあります。
はまるととても便利なのですが、日常生活ではあまり用途が思い浮かばないものでもあります。
そこで、思いついた使い道を記しておくことにします。

今回は積立投資信託ポートフォリオのリバランス時を想定します。

投資信託で分散投資をする場合、投資する地域と金融商品の組み合わせを考えることになります。
例えば、投資先地域として
  • 日本
  • 日本以外の先進国
金融商品として
  • 債権
  • 株式
がありますと、組み合わせとしては
  1. 日本債権
  2. 日本株式
  3. 先進国債権
  4. 先進国株式
となります。

1~4の配分割合を決めて投資信託を購入し保有していますと、市場の動きによって次第に価格が変化しますので、1~4の割合が購入当初からはずれてきます。このずれを修正するのがリバランスです。

購入時、配分を














としていたものが、価格変動によって














と変化したため、これを購入時の配分割合に戻すことを考えます。戻す方法は、保有している4種類の投資信託をさらに買い増すこととします。

どの資産をいくら分買えば元の割合に戻るのか。パッと金額を出すのは難しいものです。
そこで、ソルバーの出番となります。

買い増すための資金には普通上限がありますので、4資産買い増し金額の合計=資金上限とし、各資産のリバランス後の割合を制約条件としてソルバーを回せば、各資産をいくら分買い増せばよいのかが簡単に算出できます。

2014年9月30日火曜日

Google Ads | Bloggerの場合のAdSenseの始め方7

申請していたAdsenseへの申し込みがやっと承認されました。

申し込んだのが8月上旬でしたので、1ヶ月半かかりました。

2014年9月5日金曜日

Python | Excelワークシートの範囲指定方法

Excelを操作するときに必ず必要となるのが、データ範囲の指定です。Excelワークシートからデータを取得するときも、逆にデータを書き込むときも、ワークシート上の場所を指定する必要があるからです。

---

myRange1 = xlSheet.Cells(4,1) #1つのセルを(row,column)形式で指定
myRange2 = xlSheet.Range("B5:C10") #データ範囲をExcel式で指定
myRange3 = xlSheet.Range(xlSheet.Cells(2,2),xlSheet.Cells(3,8)) #データ範囲を(row,column)形式で指定

---

Python | Excelで新しいワークシートを追加する

---以下スクリプト---

import win32com.client #

xlApp = win32com.client.Dispatch("Excel.Application")

xlApp.Visible = 1

xlApp.Workbooks.open(r"C:\test.xlsx")
xlSheet = xlApp.Workbooks(1).Sheets(1)

xlApp.Worksheets.Add()

---以上スクリプト---

2014年9月2日火曜日

Python | Excelファイルの読み込み方法

Pythonを使ってExcelファイルを操作する方法です。

まずはファイルを読み込んで、指定のセルの値を表示してみます。

---以下スクリプト---

import win32com.client #

xlApp = win32com.client.Dispatch("Excel.Application")

xlApp.Visible = 1

xlApp.Workbooks.open(r"C:\test.xlsx")
xlSheet = xlApp.Workbooks(1).Sheets(1)
xlCell = xlSheet.Cells(7, 3)
value = xlCell.value

xlApp.Quit()

print "Cell(7,3)->", value

---以上スクリプト---

まず、Excelを使うのにwin32comとExcelがインストールされている必要があります。

Python用Windows extensionダウンロードサイト
http://sourceforge.net/projects/pywin32/

その上でimportして使います。

上の例では読み込んだExcelファイルのワークシート1にあるセルC7の値を取得して表示します。
xlCell = xlSheet.Cells(7, 3)ではセル位置を(row, column)で指定するため、

(7, 3) = C7

となります。

2014年8月31日日曜日

Google Apps Script | 削除済みスプレッドシートのスクリプトの挙動

トリガーを設定したGoogle Apps Scriptを適用したスプレッドシートを削除した場合、スクリプトは起動しなくなるのでしょうか?

答えはNo。

つまり、削除後もスクリプトは起動し続けます。

Google SpreadsheetをDriveから削除すると、Sheetはゴミ箱に移動します。
この状態ではスクリプトは動き続けますので、止めるにはSheetをゴミ箱から完全に削除します。

2014年8月27日水曜日

Google Ads | Bloggerの場合のAdsenseの始め方6

以前の投稿で、BloggerサイトへのAdSense登録申請を行いました。

その後約20日、ずっと審査中となっています。
これは通常通りなのか、それとも異常なのでしょうか。

2014年8月11日月曜日

Google Apps Script | メールにファイルを添付して送付する

メールだけを送るときのGoogle Apps Scriptの書き方は以前の投稿に書きました。

今回はさらにメールにファイルを添付して送信する場合の書き方です。

---以下スクリプト---

var mailTo = "___@gmail.com"; //メール送信先アドレス
var mailSubject = "Test"; // メールのタイトルvar mailBody = "これはテストです。"; // メール本文

var file1 = {

                fileName: "chart1", 
                mimeType: blob1.getContentType(),
                content: blob1.getBytes()

               };
var file2 = {
                fileName: "chart2", 
                mimeType: blob2.getContentType(),
                content: blob2.getBytes()

               };

GmailApp.sendEmail(mailTo,subject,mailBody,{attachments: [file1,file2]});

---以上スクリプト---

と、このような形式になるのですが、これでは不完全です。blob1やblob2って何?となります。これについては別途。。。

Google Apps Script | メールを送る

GmailApp.sendEmail(mailTo,mailSubject,mailBody);

ここで、mailTo, mailSubject, mailBodyは変数です。
あらかじめ

var mailTo = "___@gmail.com"; // メール送信先アドレス
var mailSubject = "Test"; // メールのタイトル
var mailBody = "これはテストです。"; // メール本文

などと指定しておきます。

Google Apps Scriptリファレンス


2014年8月10日日曜日

Google Ads | Bloggerの場合のAdsenseの始め方5

Google AdSenseを適用するサイトが承認されると、Google AdSenseオンライン利用規約への同意を求められます。

下のほうで同意するかどうかを尋ねられています。

利用規約そのものはこちらで確認できます。

Google Ads | Bloggerの場合のAdsenseの始め方4

AdSenseアカウントと収益支払い(受取り)に必要な項目を入力します。


  • 国,地域
  • タイムゾーン
  • アカウントの種類(個人またはビジネス)



次に、収益受取り人の情報を入力します。

  • 名前
  • 住所
  • 電話番号
住所は町名や番地を先に入力し、市区町村が後に入力するようになっています。日本式とは逆なので注意します。

訂正します。
国または地域として日本を選択すると、タイムゾーンと支払い受取人の入力欄は日本式に自動的に変更されます。

申し込みの送信が完了すると画面が切り替わります。

とりあえずこれでAdSense登録申請は終了です。この後審査があり、受理/却下の連絡が1週間以内にあります。

続く→Bloggerの場合のAdsenseの始め方5


Google Ads | Bloggerの場合のAdsenseの始め方3

AdSense登録するGoogleアカウントを指定して先へ進むと、左のようなページに移ります。

ここでは広告の掲載先サイトURLを確認します。Bloggerの管理ページから手順に従って進んでくると、Bloggerで作成したブログのURLが自動的に表示されます。

次に、ブログで使っている主な言語を選択し、『続行』をクリックします。


続く→Bloggerの場合のAdsenseの始め方4

SyntaxHighlighter