メインコンテンツへスキップ
見出し画像

Python基礎6:標準ライブラリ:ファイル/url操作(os, glob, shutil, urllib)

    1.概要

     今回はPython標準ライブラリによるファイル操作を紹介します。なお注意点して私はWindowsのPCで作業しておりエスケープシーケンスの都合上詳細は参考記事参照パスの記載方法がMacとは異なる可能性があります。

    ●os:ファイル操作全般で使用
    ●shutil:osで対応できない処理で使用
    ●glob:ファイル/ディレクトリの一覧リストを取得
    ●pathlib:os.pathでできることは何でもできる。
    ●urllib:URLを操作してパス取得やHTTPクライアントが実行可能

     ライブラリと関係なくファイル作成しておりコードは下記です。

    [In] ファイル作成
    def maketext(filename='test.txt'):
        with open(filename, 'w') as f:
            f.write('Hello, world!\n')
            
    maketext(filename='test.txt')  
    画像

    2.os

     標準ライブラリのosはファイル操作が得意なライブラリです。

    2-1.PC/ファイル情報取得

     osを使用することでPC/OS情報の取得ができます。普段ほとんど使わないですが

    [In]
    import os
    
    os.stat('test.txt') #ファイル情報(サイズ、更新日時、アクセス日時)を取得
    os.name #OS名称:Windows->'nt', Linux->'posix'
    os.getenv('PATH') #環境変数の値を取得
    os.getlogin() #ログインユーザー名を取得
    os.cpu_count() #CPU数を取得
    os.sep #ファイルパスのセパレータ Windows->'\\', Linux->'/'
    [Out] ※PCはWindowsです。 #os .stat('test.txt')
    os.stat_result(st_mode=33206, st_ino=281474978370894, st_dev=1279914657, st_nlink=1, st_uid=0, st_gid=0, st_size=15, st_atime=1642770342, st_mtime=1642770341, st_ctime=1642736745)
    # os.name
    'nt' 
    # os.getenv('PATH')
    'C:\\Users\\KIYO\\Anaconda3;C:\\Users\\KIYO\\Anaconda3\\Library\\mingw-w64\\bin; 以下略・・・'
    # os.getlogin()
    'KIYO' 
    # os.cpu_count()
    16 
    # os.sep
    '\\' 

    2-2.作業ディレクトリの操作・取得

     2-2-1.作業ディレクトリのパス取得:os.getcwd()

       osではパス取得や移動ができます。現在作業中のディレクトリを取得するためにはos.getcwd()を使用します。

    [IN]
    import os 
    
    filepath = os.getcwd() #現在の作業ディレクトリを取得
    print(filepath)
    [Out]
    'c:\\Users\\KIYO\\Desktop\\note\\01_Python基礎\\note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys'

     2-2-2.作業ディレクトリの移動:os.chdir('path')

     作業ディレクトリを移動する場合はos.chdir('path')を使用します。。

    [IN]
    os.chdir('c:\\Users\\KIYO\\Desktop') #現在の作業ディレクトリを変更:デスクトップに移動
    print(os.getcwd())
    os.chdir('../') #現在の作業ディレクトリの親フォルダに移動
    print(os.getcwd())
    os.chdir(filepath) #元の作業ディレクトリに移動
    print(os.getcwd())
    [OUT]
    c:\Users\KIYO\Desktop
    c:\Users\KIYO
    c:\Users\KIYO\Desktop\note\01_Python基礎\note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys

     2-2-3.絶対パスの取得:os.path.abspath('path')

     相対パスから絶対パスを取得したい場合はos.path.abspath('path')です。

    [In]
    os.path.abspath('test.txt') #絶対パスを取得
    
    [Out]
    'c:\\Users\\KIYO\\Desktop\\note\\01_Python基礎\\note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys\\test.txt'

     2-2-4.使用ファイルのpath:os.path.abspath(__file__)

     使用しているファイルのパスを取得をする場合はos.path.abspath(__file__)を使用します。注意点してJupyterでは使用できないためpyファイルを作成してコマンドラインターミナル経由でで処理する必要があります。

    [test.py]
    import os
    print(os.path.abspath(__file__))
    画像

    2-3.ファイル操作(作成・削除・名前変更)

     ファイルやディレクトリフォルダの作成および削除のメソッドはそれぞれの特徴も含めて下記の通りです。
     またファイル・フォルダの名前を変える場合はos.rename('指定ファイルパス', '変更後パス')とします。

    画像

     参考用コードは下記の通りです(Gifはtime.sleep()追加ゆっくり見せるため)。

    [In]
    import os
    
    os.mkdir('作成dir') #フォルダを作成
    os.rename('作成dir', '変更dir') #フォルダ名を変更
    os.rmdir('変更dir') #フォルダを削除
    
    maketext(filename='test.txt') #自作関数:test.txtを作成
    os.remove('test.txt') #ファイルを削除
    画像

    2-4.ファイルパスの操作:os.path

     os.pathメソッドを使用することでファイルの種類・名前・存在有無などを確認できます。説明用として適当にフォルダを作成します。

    [In]
    extensions = ['txt', 'py', 'docx', 'xlsx']
    os.mkdir('practice') #practiceフォルダを作成
    for i in range(3):
        for extension in extensions:
            maketext(f'practice/test{i}.{extension}')
    画像

     2-4-1.ファイルの存在・情報確認:os.path.exists(), os.path.isdir()

     ファイルの存在有無を確認する場合はos.path.exists('path')、指定パスがディレクトリかどうかを確認する場合はos.path.isdir()を使用します。

    [In]ディレクトリの確認
    os.path.isdir('practice') #フォルダか確認 ->True/False
    os.path.isdir('test.txt') #フォルダか確認 ->True/False
    
    [Out]
    True
    False
    [In] ファイルの存在確認
    os.path.exists('practice') #ファイル/ディレクトリの存在確認 ->True/False
    os.path.exists('practice/test0.txt') #ファイル/ディレクトリの存在確認 ->True/False
    os.path.exists('practice/test100.txt') #ファイル/ディレクトリの存在確認 ->True/False
    
    [Out]
    True
    True
    False

     os.path.exists()はファイル・ディレクトリが存在しない場合に作成するような処理を作ることが出来ます。

    [In]
    def mkdir_dircheck(dirname):
        if not os.path.exists(dirname):
            os.mkdir(dirname)
            print(f'{dirname}フォルダを作成しました')
        else:
            print(f'{dirname}フォルダはすでに存在しています')
    
    mkdir_dircheck('practice')
    
    
    [Out]
    practiceフォルダはすでに存在しています

     2-4-2.ファイル名・拡張子の取得

    [In]
    abspath = os.path.abspath('test.txt') #絶対パスを取得
    print(abspath)
    
    os.path.dirname(abspath) #ファイル/ディレクトリの親フォルダを取得
    os.path.basename(abspath) #ファイル名取得
    os.path.splitext(abspath) #拡張子を除いたファイル名と拡張子を取得
    [Out]
    #abspath
    C:\Users\KIYO\Desktop\note\01_Python基礎\note_python基礎6_os, glob, shutil, sys\test.txt
    
    #os.path.dirname(abspath), os.path.basename(abspath), os.path.splitext(abspath)
    C:\Users\KIYO\Desktop\note\01_Python基礎\note_python基礎6_os, glob, shutil, sys
    test.txt
    ('C:\\Users\\KIYO\\Desktop\\note\\01_Python基礎\\note_python基礎6_os, glob, shutil, sys\\test',
     '.txt')

     2-4-3.パスの結合:os.path.join()

     os.path.join()でPathの結合が可能です。

    [In]
    os.path.join(r'C:\Users\KIYO\Desktop', 'test.txt')
    
    [Out]
    'C:\\Users\\KIYO\\Desktop\\test.txt'

     2-4-4.パスの分離:os.path.split()

     Pathを親フォルダとファイル名に分離したい場合はos.path.split(path)を使用します。

    [IN]
    import os
    
    path = 'C:\\Users\\KIYO\\Desktop\\test.txt'
    os.path.split(path)
    
    [OUT]
    ('C:\\Users\\KIYO\\Desktop', 'test.txt')

    2-5.フォルダ/ファイル検索

     2-5-1.指定パスのファイル検索:os.listdir()

     指定したパスと同じ位置にあるファイル・フォルダを検索するのはos.listdir()を使用します。

    [IN]
    import os
    
    path_cwd = os.getcwd()
    files = os.listdir(path_cwd)
    
    print(files)
    
    [OUT]
    ['.ipynb_checkpoints', 'AIMap_JP_20200611.pdf', 'company_list.csv', 'Compe', 'desktop.ini', 'docker', 'Git', 'KIYO - Chrome.lnk', 'Music', 'note', 'Programming', 'Youtube', '練習用NB.ipynb']

     出力で確認できるようにショートカットや隠しファイルも取得されるため、必要であればほしいものを抽出できる関数の追加が必要です。

    [IN]
    import os
    
    path_cwd = os.getcwd()
    files = os.listdir(path_cwd)
    
    path_dirs = [i for i in files if os.path.isdir(i) and i[0]!="."] #フォルダのみ抽出/隠しファイル(頭に.)は除外 
    print(path_dirs)
    
    def extract_file(paths):
      output = []
      for path in paths:
        if os.path.isdir(path): #フォルダの場合
          pass
        elif path[0]==".": #隠しファイルの場合
          pass
        elif path[-4:]==".lnk": #リンクファイルの場合
          pass
        elif path[-4:]==".ini": #iniファイルの場合
          pass
        else:
          output.append(path)
      return output
    
    files_ext = extract_file(files)
    print(files_ext)
    
    [OUT]
    ['Compe', 'docker', 'Git', 'Music', 'note', 'Programming', 'Youtube']
    ['AIMap_JP_20200611.pdf', 'company_list.csv', '練習用NB.ipynb']

     2-5ー2.全部のパスを探索:os.walk()

     指定したディレクトリ以下にあるフォルダおよびファイルをすべて取得したい場合はos.walk()を使用します。例として下記フォルダで処理します。

    画像
    [In] ※note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys.ipynb
    for root, dirs, files in os.walk("./"):
        print("検索ディレクトリ: " , root)
        print("ディレクトリ内のフォルダ:" , dirs)
        print("ディレクトリ内のファイル: " , files)
    
    [Out]
    検索ディレクトリ:  ./
    ディレクトリ内のフォルダ: ['note_python基礎0_pythonライブラリの紹介', 'note_python基礎1_List', 'note_python基礎2_Dict', 'note_python基礎3_文字列操作', 'note_python基礎4_class', 'note_python基礎5_関数', 'note_python基礎6_os, glob, shutil, sys', 'note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys', 'note_python基礎_tkinter', 'note_python番外編1_ID, PASS管理', 'VS CODE 環境構築']
    ディレクトリ内のファイル:  ['note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys.ipynb']
    検索ディレクトリ:  ./note_python基礎0_pythonライブラリの紹介
    ディレクトリ内のフォルダ: []
    ディレクトリ内のファイル:  ['note_見出し.jpg']
    検索ディレクトリ:  ./note_python基礎1_List
    ディレクトリ内のフォルダ: []
    ディレクトリ内のファイル:  ['note1.ipynb', 'note1_List説明用 (1).png', 'note1_List説明用.drawio', 'note1_List説明用.png', 'note_見出し.jpg']
    検索ディレクトリ:  ./note_python基礎2_Dict
    ディレクトリ内のフォルダ: ['Archive']
    ディレクトリ内のファイル:  ['dict説明1.png', 'note_python基礎2_Dict.ipynb', 'note_見出しnote_python基礎2_Dict.jpg']
    
    以下省略

     条件分岐などでほしいファイルを検索することも可能です。

    3.シェル操作:shutil(Shell Utilities)

     ShutilとはShell Utilitiesの略であり高度なファイル処理ができます。具体的にはosでできなかった処理ができるようになります。

    3-1.コピー:shutil.copy(), shutil.copytree()

      ファイルのコピーはshutil.copy()を使用します。またファイルが入ったフォルダごとコピーしたい場合はshutil.copytree()を使用します。

    [In]
    import shutil 
    
    shutil.copy('test.txt', 'test2.txt') #ファイルをコピー
    shutil.copytree('practice', 'practice2') #フォルダをコピー
    画像

    3-2.削除:shutil.rmtree

     os.rmdir()ではできなかったファイルが存在するフォルダごと削除したい場合はshutil.rmtree()を使用します。

    [In]
    shutil.rmtree('practice2') #フォルダを削除
    画像

    3-3.移動:shutil.move()

     ファイルの移動はshutil.move('filepath', '移動先dir')と記載します。

    [In]
    shutil.move('test.txt', 'practice') #ファイルを移動
    画像

    4.パスの一括取得:glob

    4-1.ファイルの一括取得:ディレクトリ直下

     globはファイルやフォルダ一覧をまとめて取得する場合に使用します。特にワイルドカード*を使用することで条件抽出可能です。

    [In]
    import glob
    
    allfiles = glob.glob('./*') #現在のディレクトリ内のファイルを取得
    files_word = glob.glob('practice/*.docx') #practiceフォルダ内のdocxファイルを取得
    files_excel = glob.glob('practice/*.xlsx') #practiceフォルダ内のxlsxファイルを取得
    
    print(allfiles)
    print(files_word)
    print(files_excel)
    [Out]
    #allfiles
    ['.\\Archive', '.\\Gif', '.\\note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys.ipynb', '.\\practice', '.\\test.py', '.\\test2.txt', '.\\説明用資料.xlsx']
    #files_word
    ['practice\\test0.docx', 'practice\\test1.docx', 'practice\\test2.docx']
    #files_excel
    ['practice\\test0.xlsx', 'practice\\test1.xlsx', 'practice\\test2.xlsx']

    4-2.再帰的に取得:recursive=True

     先ほどはディレクトリ直下のファイルのみ取得しました。globでは指定したフォルダ+サブフォルダ以下すべてのファイルも取得(再帰的)することが可能です。再帰的に取得する場合は下記を指定します。

    1. 再帰的に取得したいフォルダを"**"で指定

    2. recursive=Trueを設定

     サンプル例で説明します。まずはTxtファイルをフォルダ直下とサブフォルダに配置しました(※出力はフォルダとファイルをご確認ください)。

    [IN]
    import os
    import glob
    
     #サンプル用のフォルダを作成 
    dir_name1, dir_name2, dir_name3 = "Apple", 'Banana', 'Orange'
    subdir_name1, subdir_name2 = 'Mikan', 'Ponkan'
    
    subdir1, subdir2 = os.path.join(dir_name3, subdir_name1), os.path.join(dir_name3, subdir_name2) #フォルダ作成 
    names_dir = [dir_name1, dir_name2, dir_name3, subdir1, subdir2]
    for name in names_dir:
        if not os.path.exists(name):
            os.makedirs(name)
            
     #textファイルを作成 
    names_dir2 = [dir_name1, dir_name2, subdir1, subdir2]
    for name in names_dir2:
        print(name)
        for i in range(2):
            file_path = os.path.join(name, f'{os.path.basename(name)}_{i}.txt')
            with open(file_path, 'w') as f:
                f.write(f'{os.path.basename(name)}_{i}')
    
    [OUT]
    .
    ├── Apple
    │   ├── Apple_0.txt
    │   └── Apple_1.txt
    ├── Banana
    │   ├── Banana_0.txt
    │   └── Banana_1.txt
    ├── NB_Template.ipynb
    └── Orange
        ├── Mikan
        │   ├── Mikan_0.txt
        │   └── Mikan_1.txt
        └── Ponkan
            ├── Ponkan_0.txt
            └── Ponkan_1.txt

     テストとして①通常、②"**"+引数なし、③"**"+引数ありで作成しました。結果として③は再帰的にファイルを検索できました。

    [IN]
    paths1 = glob.glob('*/*.txt')
    paths2 = glob.glob('**/*.txt')
    paths2_recursive = glob.glob('**/*.txt', recursive=True)
    
    print(f'ファイル数 通常:{len(paths1)}, 再帰:{len(paths2_recursive)}')
    
    print(paths1)
    print(paths2)
    print(paths2_recursive)
    
    
    [OUT]
    ファイル数 通常:4, 再帰:8
    ['Apple\\Apple_0.txt', 'Apple\\Apple_1.txt', 'Banana\\Banana_0.txt', 'Banana\\Banana_1.txt']
    ['Apple\\Apple_0.txt', 'Apple\\Apple_1.txt', 'Banana\\Banana_0.txt', 'Banana\\Banana_1.txt']
    ['Apple\\Apple_0.txt', 'Apple\\Apple_1.txt', 'Banana\\Banana_0.txt', 'Banana\\Banana_1.txt', 'Orange\\Mikan\\Mikan_0.txt', 'Orange\\Mikan\\Mikan_1.txt', 'Orange\\Ponkan\\Ponkan_0.txt', 'Orange\\Ponkan\\Ponkan_1.txt']

    5.ファイルパス操作:pathlib

     ファイルやディレクトリ(フォルダ)のパスを取得できるライブラリであり、相対パス↔絶対パス変換などが可能です。

    [IN]
    import pathlib
    
    cwdir = pathlib.Path.cwd() #現在の作業ディレクトリを取得:os.getcwd()と同じ
    print(cwdir)
    print(cwdir.relative_to(cwdir)) #現在の作業ディレクトリからの相対パスを取得
    
    
    path_rel = pathlib.Path('test.txt') #現在の作業ディレクトリからの相対パスを取得
    print(path_rel) 
    print(path_rel.resolve()) 
    print(path_rel.absolute()) #絶対パスを取得
    
    [OUT]
    c:\Users\KIYO\Desktop\note\01_Python基礎\note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys
    .
    test.txt
    test.txt
    c:\Users\KIYO\Desktop\note\01_Python基礎\note_python基礎6_標準ライブラリ(ファイル操作)_os, glob, shutil, sys\test.txt

    6.URL操作:urllib

     URL操作をする標準ライブラリとしてurllibがあります。こちらを使用するとパス結合によるURL作成や指定URLから画像の取得などができます。

     なお一部で似たような処理はrequestsで代用できます。


    6-1.URLからファイルのDL:urlretrieve()メソッド

     指定したURLから画像を抽出する場合は"urllib.request.urlretrieve(url, filename)"を使用します。サンプルとして「作成ノート(+予定)一覧 by KIYO」の画像を取得しました。

    [IN]
    import urllib.request
    import os
    
    url = 'https://assets.st-note.com/img/1661919379413-vE07W5wCwS.jpg' #画像のURL
    extension = os.path.splitext(url)[-1] #URLから拡張子を取得
    filename = '取得画像' + extension #拡張子付きのファイル名を作成
    
    urllib.request.urlretrieve(url, filename) #画像を取得
    
    [OUT]
    ('取得画像.jpg', <http.client.HTTPMessage at 0x25ffa4d6520>)
    画像

    6-2.URLからBinaryデータを取得:urlopen()

     URLからデータを取得する別の方法として"urllib.request.urlopen(url)"があります。こちらはオブジェクトを返すためread()メソッドを使用してバイナリーデータを抽出可能です。
     バイナリーデータはopen()関数でファイルに書き込むことで画像データを抽出できます。

    [IN]
    import urllib.request
    
    url = 'https://assets.st-note.com/img/1661919379413-vE07W5wCwS.jpg' #画像のURL
    
    _ = urllib.request.urlopen(url) #中身確認用
    print(_, type(_)) #オブジェクトであることを確認
    
    img_bi = urllib.request.urlopen(url).read() #画像を取得
    print(type(img_bi)) #type
    print(img_bi[:20]) #画像の最初の20バイトを確認
    
    #Binaryデータをファイルに保存
    with open('urlopenで保存.jpg', "wb") as f:
        f.write(img_bi) #Binaryデータをファイルに保存
    
    [OUT]
    <http.client.HTTPResponse object at 0x0000025FFA4D1580> <class 'http.client.HTTPResponse'>
    
    <class 'bytes'>
    b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00'
    画像

     なおHPからデータを抽出する場合binaryデータとなるためdecode処理追加が必要です。

    [IN]
    import urllib.request
    
    url = 'https://note.com/kiyo_ai_note/n/nd49cc7237ceb#9e542838-ed21-4314-86ec-551ab32fe87a' #note_KIYOのURL
    
    url_bi = urllib.request.urlopen(url).read() #画像を取得
    print(url_bi[:200], end='\n\n') #画像の最初の20バイトを確認
    print(url_bi[:200].decode('utf-8')) #画像の最初の20バイトを確認
    
    [OUT]
    <class 'bytes'>b'<!doctype html>\n<html data-n-head-ssr lang="ja" data-n-head="%7B%22lang%22:%7B%22ssr%22:%22ja%22%7D%7D">\n  <head >\n    <title>\xe4\xbd\x9c\xe6\x88\x90\xe3\x83\x8e\xe3\x83\xbc\xe3\x83\x88(+\xe4\xba\x88\xe5\xae\x9a)\xe4\xb8\x80\xe8\xa6\xa7 by KIYO\xef\xbd\x9cKIYO\xef\xbd\x9cnote</title><meta data-n-h'
    
    <!doctype html>
    <html data-n-head-ssr lang="ja" data-n-head="%7B%22lang%22:%7B%22ssr%22:%22ja%22%7D%7D">
      <head >
        <title>作成ノート(+予定)一覧 by KIYO|KIYO|note</title><meta data-n-h

    6-3.クエリパラメータ:urllib.parse.urlencode()

     クエリパラメータは「pythonライブラリ:requests」で紹介していますので説明は省略しますが、urllibで辞書型データからクエリパラメータを作成できます。
     参考として下記URLを参考にクエリパラメータを作成します。作成したurlに追加処理してスクレイピングや画像取得などができます。

    画像
    [IN]
    import urllib.request
    import urllib.parse
    
    url_base = 'https://assets.st-note.com/img/1661608788550-TlhF7tjch6.png' #note_KIYOのベース
    
    value = {'width':2000,
            'height':2000,
            'fit':'bounds',
            'quality':85}
    
    params = urllib.parse.urlencode(value) #辞書型をクエリパラメータに変換
    print(params)
    url = url_base + '?' + params #ベースURLとクエリパラメータを結合
    print(url)
    
    [OUT]
    width=2000&height=2000&fit=bounds&quality=85
    https://assets.st-note.com/img/1661608788550-TlhF7tjch6.png?width=2000&height=2000&fit=bounds&quality=85

    6-4.URL結合・相対パス作成:urljoin()

     ベースとなるURLから指定したパスを相対パスとして結合したい場合は"urljoin()"を使用します。

    [IN]
    from urllib.parse import urljoin
    
    baseurl = 'https://note.com/kiyo_ai_note'
    
    print(urljoin(baseurl, 'magazines')) #BaseのURLに結合※Baseの最終パスは残らない
    print(urljoin(baseurl, 'n/n2fd4e8e938f4'))
    
    print(urljoin(baseurl, '../')) #親フォルダを指定(移動)
    print(urljoin(baseurl, '../test')) #親フォルダ/<指定パス>へ移動※urljoin(baseurl, path)と同じ
    
    [OUT]
    https://note.com/magazines
    https://note.com/n/n2fd4e8e938f4
    https://note.com/
    https://note.com/test

    7.システムパラメーター/パス管理:sys

     システムパラメーター/パス管理の標準ライブラリsysは下記記事に別でまとめました。

    参考記事

    あとがき

     仕事でVBA使ってるけどPythonと同じ操作がExcelで簡単にできたらどれだけ楽なことか・・・・・・・


     
     

    KIYO

     
     
    普段は製造業で企画/開発/設計しております。記事はプログラミング・機械学習、IoT関係の記事をメインで作成し、なるべく1つの記事で知りたいことを網羅していきます。内容は学術的より実装・アウトプット(ほしくなるもの)を重視して作成しています。 面白そうな仕事があればやりたいです!

    あなたへのおすすめ