Hatena::ブログ(Diary)

bonlife このページをアンテナに追加 RSSフィード

言及ISBN/ASIN
  • Ringo EXPO 08 [DVD]
  • 三文ゴシップ
  • my way
  • ビジネスパーソンのための話し方入門 (日経文庫)
  • ザ・グーグルウェイ グーグルを成功へ導いた型破りな戦略
  • 考え・書き・話す3つの魔法
  • 自分の答えのつくりかた―INDEPENDENT MIND

2015-11-30

Pythonで指定したディレクトリ配下のディレクトリ名とファイル名を取得する(改定案)

http://statsbeginner.hatenablog.com/entry/2015/11/26/134250

import os

def gen_filelists(path, abspath=False, sysfile=False):
    # 指定ディレクトリの配下にあるディレクトリやファイルをたどる
    for dirpath, dirnames, files in os.walk(path):
        # sysfile=False の場合、システムディレクトリ(.ではじまるもの)を対象外にする
        for dir in dirnames:
            if sysfile==False:
                if dir.startswith('.'):
                    dirnames.remove(dir)                
        for file in files:
            # sysfile=False の場合、システムファイル(.ではじまるもの)を無視
            if sysfile==False:
                if file.startswith('.'):
                    pass
                else:
                   # フルパスを出力するかどうかで処理を分岐
                    if abspath==True:
                        yield os.path.abspath(file)
                    else:
                        yield file
            # 下記パートが重複している点、カッコ悪いので改善が必要…
            else:
                if abspath==True:
                    yield os.path.abspath(file)
                else:
                    yield file

if __name__ == "__main__":
    print("abspath:False / sysfile:False")
    print([ file for file in gen_filelists('.', abspath=False, sysfile=False) ])
    # sysfile=True にした場合、Windowsでは os.path.abspath() が . で始まるフォルダを上手く扱えない?
    print("abspath:False / sysfile:True")
    print([ file for file in gen_filelists('.', abspath=False, sysfile=True ) ])
    print("abspath:True  / sysfile:False")
    print([ file for file in gen_filelists('.', abspath=True,  sysfile=False) ])
    print("abspath:True  / sysfile:True")
    print([ file for file in gen_filelists('.', abspath=True,  sysfile=True ) ])

2009-01-31 明日は義妹の結婚式です。

Pythonでctypesやらwin32apiを使ってドライブの空き容量を調べるサンプル

bonlifeです。久しぶりにPythonに触れてみました。

コピーするフォルダーの容量は、os.walkを使って取得できました が、ドライブの空き容量の求め方がわかりません。仕方がないので、dirコマンドの出力を正規表現で切り出してみました。日本語正規表現がわからないので、dos窓英語モードにしてからdirコマンドを発行しています。

ドライブの残り容量を調べるpythonスクリプト ? lights on zope

なんだかあんまりスマートじゃないやり方…。こういうことは、Windowsに直接語りかければ答えが返ってくるはず!ということで調べてみると、GetDiskFreeSpaceExというものがありました。以下のような関数らしい。lpDirectoryName を指定して実行するとアレコレ情報が取得できるみたい。

BOOL GetDiskFreeSpaceEx(
  LPCTSTR lpDirectoryName,                 // ディレクトリ名
  PULARGE_INTEGER lpFreeBytesAvailable,    // 呼び出し側が利用できるバイト数
  PULARGE_INTEGER lpTotalNumberOfBytes,    // ディスク全体のバイト数
  PULARGE_INTEGER lpTotalNumberOfFreeBytes // ディスク全体の空きバイト数
);

ctypesを使ってみる

こういうのはctypes使うと良いってどこかで見かけたことがあったので、ctypesとGetDiskFreeSpaceExを元にGoogle先生にお伺いをたてたところ、まさにッ!なページを発見。

http://www.python-forum.org/pythonforum/viewtopic.php?f=18&t=9461#p43424

このままだとドライブを指定できなかったり、戻り値が ctypes.c_ulonglong で扱いづらかったりするので、少しだけ修正してみました。(失敗した場合の処理は省略。)

>>> from ctypes import *
>>> def free_disk_space(drive=None):
	free_bytes_available       = c_ulonglong()
	total_number_of_bytes      = c_ulonglong()
	total_number_of_free_bytes = c_ulonglong()
	a = windll.kernel32.GetDiskFreeSpaceExA(
		drive,
		byref(free_bytes_available),
		byref(total_number_of_bytes),
		byref(total_number_of_free_bytes)
	)
	return free_bytes_available.value, total_number_of_bytes.value, total_number_of_free_bytes.value

>>> free_disk_space('C:')
(397914681344L, 483934597120L, 397914681344L)
>>> free_disk_space()
(397914681344L, 483934597120L, 397914681344L)
>>> for i in free_disk_space('C:'):
	print i / (1024 ** 3), "GB"

370 GB
450 GB
370 GB

win32apiを使ってみる

ただ、pywin32インストールしてwin32apiを使うと遥かに簡単に同じことが出来ちゃいます。

>>> import win32api
>>> win32api.GetDiskFreeSpaceEx('C:')
(397914681344L, 483934597120L, 397914681344L)
>>> for i in win32api.GetDiskFreeSpaceEx():
	print i / (1024 ** 3), "GB"

	
370 GB
450 GB
370 GB

楽過ぎる。pywin32は今日インストールしてはじめて使ってみましたが、IDLEでの補完も利くし、色々と遊べそうですね。

まとめ

スクレイピング的な処理をせずに情報を取得できないか調べてみたことで、bonlifeの視界が広がりましたとさ。Windowsの操作で何か困った時、今回の経験が活きますように。

2008-10-28 何が正しいのかが分からなくなってきました。

timeitで計測

bonlifeです。PowerPointでの資料作りに嫌気がさしたので、久しぶりにPythonに触れてみました。

宿題として、華麗にTimerを使いこなせるようになる事が追加されました。

Pythonでスクレイピングに最適なライブラリはlxmlな気がした。時間的な意味で - a2c.get.diary

ちょっと違うかもしれないけど、こういうことかしら。(って普通過ぎるTimerの使い方ですね…。華麗ではない。)

In [1]: from time import sleep

In [2]: from timeit import Timer

In [3]: def hoge():
   ...:     sleep(0.01)
   ...:
   ...:

In [4]: def fuga():
   ...:     sleep(0.02)
   ...:
   ...:

In [5]: def piyo():
   ...:     sleep(0.03)
   ...:
   ...:

In [6]: t1 = Timer(setup='from __main__ import hoge', stmt='hoge()')

In [7]: t2 = Timer(setup='from __main__ import fuga', stmt='fuga()')

In [8]: t3 = Timer(setup='from __main__ import piyo', stmt='piyo()')

In [9]: for i in (t1, t2, t3):
  ....:     i.timeit(100) / 100
  ....:
  ....:
Out[9]: 0.010735489802108589
Out[9]: 0.020498639798942123
Out[9]: 0.030266023705639498

t1, t2, t3を生成する部分を関数にしたりすれば!とか思いましたが、馬鹿馬鹿しいのでやめましたとさ。

2008-04-17 髪が伸びすぎて大変なことになってます。

「Pythonには反応しとくか」に反応しとくか

bonlifeです。id:Isoparametric:20080417:1208387767でやりたいのってひょっとしてこういうことかしら。全く違ったらゴメンナサイです。

In [1]: hoge_list = [ range(i,i+16) for i in range(0,16*4,16) ]

In [2]: hoge_list
Out[2]:
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
 [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
 [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],
 [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]]

In [3]: for hoge in hoge_list:
   ...:     print hoge
   ...:     idx, data1, data2, data3 = hoge = hoge[:4]
   ...:     print hoge
   ...:     print idx, data1, data2, data3
   ...:
   ...:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[0, 1, 2, 3]
0 1 2 3
[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
[16, 17, 18, 19]
16 17 18 19
[32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]
[32, 33, 34, 35]
32 33 34 35
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
[48, 49, 50, 51]
48 49 50 51

In [4]: hoge_list
Out[4]:
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
 [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
 [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],
 [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]]

In [5]: for i, hoge in enumerate(hoge_list):
   ...:     print hoge
   ...:     idx, data1, data2, data3 = hoge = hoge[:4]
   ...:     print hoge
   ...:     print idx, data1, data2, data3
   ...:     hoge_list[i] = hoge
   ...:
   ...:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[0, 1, 2, 3]
0 1 2 3
[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
[16, 17, 18, 19]
16 17 18 19
[32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]
[32, 33, 34, 35]
32 33 34 35
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
[48, 49, 50, 51]
48 49 50 51

In [6]: hoge_list
Out[6]: [[0, 1, 2, 3], [16, 17, 18, 19], [32, 33, 34, 35], [48, 49, 50, 51]]

リストをスライスした結果として返ってくるリストを変数に代入して、代入が返すリストを各変数にバラしてみましたよ。右から左に値が渡っていくあの感じ。

ちなみに、私はPython大好きです!

2008-04-13 丸一日マリオカートです。

URLをリンクに変更する (WebHelpersのauto_link)

マリオカートやり過ぎて目がシバシバしているbonlifeです。この記事を見て、そういうのって色んな人が似たようなことやってるのよね、きっと。と思って少しだけ調べてみました。

正規表現を使ってURLをリンクにする。

URLをリンクに変更する

それっぽいキーワードで適当に検索したところ、Pylonsで使われているWebHelpersにRailsにインスパイアされた機能があることが判明。

WebHelpersのauto_link_urls

AUTO_LINK_RE = re.compile(r"""
                        (                          # leading text
                          <\w+.*?>|                # leading HTML tag, or
                          [^=!:'"/]|               # leading punctuation, or 
                          ^                        # beginning of line
                        )
                        (
                          (?:https?://)|           # protocol spec, or
                          (?:www\.)                # www.*
                        ) 
                        (
                          [-\w]+                   # subdomain or domain
                          (?:\.[-\w]+)*            # remaining subdomains or domain
                          (?::\d+)?                # port
                          (?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path
                          (?:\?[\w\+%&=.;-]+)?     # query string
                          (?:\#[\w\-]*)?           # trailing anchor
                        )
                        ([\.,"'?!;:]|\s|<|$)       # trailing text
                           """, re.X)

捕捉しないカッコが出るだけで難しく見えるマジックですね。この正規表現をこんな関数で使う。

def auto_link_urls(text, **href_options):
    extra_options = tag_options(**href_options)
    def handle_match(matchobj):
        all = matchobj.group()
        a, b, c, d = matchobj.group(1, 2, 3, 4)
        if re.match(r'<a\s', a, re.I):
            return all
        text = b + c
        if b == "www.":
            b = "http://www."
        return '%s<a href="%s%s"%s>%s</a>%s' % (a, b, c, extra_options, text, d)
    return re.sub(AUTO_LINK_RE, handle_match, text)

<a で始まってたら、処理せずにそのまま返して、www. 始まりだったら http:// をつけるのね。正規表現の h の後にも ? を追加して ttp:// はじまりにも対応したら良いのに。ってこれは日本限定の文化かな。

WebHelpersのauto_link_email_addresses

ちなみにメールアドレス用の正規表現もそれらしい関数に含まれてましたよ。

def auto_link_email_addresses(text):
    return re.sub(r'([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)',
                  r'<a href="mailto:\1">\1</a>', text)

ふーん。何で \w 使ったり使わなかったりなんだろう。後半部分でも \w 使えますよね。って思って調べてみたら、\w だと _ (アンダースコア)も含んでしまうんですね。だから外してるのか。これくらいがザル過ぎず、複雑になり過ぎない絶妙なラインってことなのかしら。って納得しかけたけど、ローカルパート部分に . (ドット)が2回出てくる?アレレ。 + の次にあるエスケープされていないドットは何なんだろう…。文字クラスの書き方が分からなくなってきましたが、とりあえず保留。

BeautifulSoupのfindAllではrecursiveが設定できる

ケータイのキャリアをNTTドコモからSoftBankに変えてしまったbonlifeです。

BeautifulSoupのfindAllではその子要素以下まで全て見つけてきてしまうようだ。

そこで、自分が誰の子要素かを判断するために「parent」を使う。

[BeautifulSoup

BeautifulSoupのfindAllについてのこんな記事がありました。アプローチとしては正しいと思いますが、別解もありますよね。

BeautifulSoupのrecursiveをFalseにする


In [3]: from BeautifulSoup import BeautifulStoneSoup

In [4]: xml = """<?xml ?>
   ...: <list>
   ...:   <item>test11</item>
   ...:   <sublist>
   ...:     <item>test21</item>
   ...:     <item>test22</item>
   ...:   </sublist>
   ...:   <item>test12</item>
   ...: </list>"""

In [5]: soup = BeautifulStoneSoup(xml)

In [6]: for e in soup.findAll('item'):
   ...:     print e
   ...:
   ...:
<item>test11</item>
<item>test21</item>
<item>test22</item>
<item>test12</item>

In [7]: for L in soup.findAll('list',recursive=False):
   ...:     for i in L.findAll('item',recursive=False):
   ...:         print i
   ...:
   ...:
<item>test11</item>
<item>test12</item>

こんな感じ。

補足

こういうのはXpathの方が向いてますぜ、ってことを説明するためにlxml使おうとしたらparseでエラー。

<class 'lxml.etree.XMLSyntaxError'>: Malformed declaration expecting version, line 1, column 6

XML宣言ではversionは省略できないんですね。ほえほえ。XML、なんとなくで使ってるので一通りちゃんと勉強したいところ。