見出し画像

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で簡単にできたらどれだけ楽なことか・・・・・・・


いいなと思ったら応援しよう!