Pythonで大量のファイルを種類別のフォルダに移動する方法|Python自動化のできること

こんにちは。キノコードです。
みなさん、仕事で大量のファイル移動やコピーをする機会はありませんか?
その作業が面倒で、億劫で、自動化したいと思ったことはありませんか?
その作業をPythonで自動化をしてみましょう。
このレッスンをみていただくとPythonを使ったファイルのコピー、移動、削除などができるようになり、
最終的にはコードを実行するだけで「ファイルの種類ごとのフォルダを自動作成して、種類ごとのフォルダに自動でコピーをする」ということができるようになります。
以前に定期実行をする動画を出しました。
この動画を組み合わせれば、定期的に特定にあるフォルダにあるファイルを、指定したフォルダにコピーや移動することもできます。
また日付ごとにフォルダに振り分けることもできるでしょう。
さらには、定期的にファイルを移動して、フォルダを削除したりすることもできます。
面倒な仕事をPythonに任せましょう。
キノコードでは、このような自動化のレッスンをたくさんアップしています。
他にもPythonを使った集計のレッスンを多くアップしています。
また、Pythonを使ったデータ集計のレッスン、データをグラフ化するレッスン、人工知能のレッスンもアップしています。
今後は、Webアプリケーション開発の動画や、データ分析に必須のSQLの講座も配信していく予定です。
チャンネル登録していただけますと新着通知がいきますので、ぜひチャンネル登録をお願いします。

この記事の信頼性

この記事は、Youtubeにて日本最大級のプログラミング教育のチャンネルを運営しているキノコードが執筆、監修しています。
私自身は、2012年からプログラミング学習を始め、2019年以降はプログラミング教育に携わってきた専門家です。
他にも、私には下記のような実績や専門性があります。

・キノコードは毎月10名以上、合計100名以上ののプログラミング学習者と1対1でお悩みを聞き、アドバイスをしています
・キノコード自身は、プログラミングスクールに通ったり、本や有料åの動画で勉強してきた経験もあります
・キノコードは、Python学習サービス「キノクエスト」を運営しています
・本の出版、プログラミング雑誌への寄稿の実績があります

Python学習サービス「キノクエスト」のご紹介

キノコードでは、Pythonを習得するためのPython学習サービス「キノクエスト」を運営しています。
キノクエストには、学習カリキュラムがあり、学習順番に悩むことなく学習を進められます。
月額1,990円と本1冊分の値段です。

キノクエストの特徴は下記の通りです。
・Python学習をしている仲間が集まるコミュニティがある
・1000問以上の問題を解いてプログラミングを習得
・環境構築不要ですぐに始められる
・動画と連動しているので、インプットもできる
・月額1,990円で、コミュニティもセット

キノクエストを詳しく知りたい方は、紹介ページをご覧ください。

▼キノクエストの紹介ページはこちら▼
https://kino-code.com/kq_service_a/

それでは解説をはじめます。

用語の解説と操作するファイルの説明

それではレッスンに入る前に、今回のレッスンで使用するデータについて解説し、レッスンを進める上で必要な用語解説を3つほどいたします。
copy_fromというフォルダには、画像ファイルのJPGファイル、PNGファイル、エクセルファイル、csvファイルが入っています。
数はそれぞれ、JPGが100枚、エクセルファイル26個、csvファイル26個あります。
これらのファイルをコピーするコードを解説していきます。

また3つほど用語の解説をしていきます。
最初はディレクトリという用語です。
ディレクトリは、コンピュータに保存されているデータをわけたり、まとめたりする「入れ物」のことです。
ディレクトリという単語はフォルダと捉えてもらって問題ないです。
この動画では、フォルダのことをディレクトリと呼んで説明をしていきます。
プログラマやエンジニアは、フォルダのことをディレクトリということが多いので、単語になれておきましょう。

2つ目は拡張子です
拡張子は、ファイルの種類を識別するためのつけられている、「.(ピリオド)」以降の文字列のことです。
つまり、ファイルの種類は拡張子で識別しています。
この動画では、ファイルの種類のことを拡張子と呼んで説明するとがあります。
こちらも覚えておきましょう。

3つめは、パスです。
パスとは、ファイルの場所を示すための文字列のことです。
ディレクトリ名をつなげるとパス名になります。
ディレクトリまでのパスのことをディレクトリパスといい、ファイルまでのパスのことをファイルパスといいます。

例えば、ドキュメントの中にあるKinoCodeというディレクトリに「file_move.ipynb」というファイルがあります。
このファイルパスは、
「/Users/kinocode/Documents/KinoCode/file_move.ipynb」
このファイルのディレクトリパスは
「/Users/kinocode/Documents/KinoCode/」
です。
厳密には、ファイル名を含めない場合でもファイルパスということもありますが、このレッスンでは混乱をさけるため、ファイル名を含めないパスのことをディレクトリパス、ファイル名を含めたパスのことをファイルパスということにします。

ライブラリのインポート

import os
import shutil

まず最初にライブラリをインポートしていきます。
今回は、osとshutil(シャティル)というライブラリを使用します。
osは、基本ソフトウェアの機能を使用するためのライブラリです。
このレッスンでは、ファイル名を取得したり、ディレクトリを作成したり、ファイル名やディレクトリを削除するために使用します。
shutilは、高水準のファイル操作を行うためのライブラリです。
高水準はたくさんのことができるぐらいのイメージで良いと思います。
今回、shutilは、ファイルのコピーなどに使用します。
「import os」「import shutil」と書いて実行します。  

ディレクトリの作成

os.mkdir('./copy_to')

まず最初はファイルをコピーをすることがからはじめていきましょう。
ファイルをコピーさせるディレクトリを作成します。
ディレクトリの作成には、osライブラリのmkdirメソッドを使用します。
os.mkdir丸括弧。丸括弧の中には、ディレクトリパスを記述し、シングルクォーテーションでくくります。
os.mkdirメソッドの使用方法は以上ですが、ディレクトリパスについて補足をします。

ここでは、copy_toというディレクトリを作成します。
したがって、このコードは、copy_toがディレクトリ名、それより前がパスになります。
このドットは、現在のディレクトリを意味しています。
現在のディレクトリとはjupyterlabを使用している場合、jupyterlabが置いてある場所になります。
この現在のディレクトリからスラッシュをつけると、下の階層という意味になります。
つまり、現在のディレクトリの下の階層に、copy_toというディレクトリを作成するというコードになります。
実行します。
新しいディレクトリが作成されました。

ファイルのコピー その1

os.mkdir('./copy_to')
---------------------------------------------------------------------------

FileExistsError                           Traceback (most recent call last)

 in 
----> 1 os.mkdir('./copy_to')

FileExistsError: [Errno 17] File exists: './copy_to'

ちなみに、mkdirメソッドは、すでにあるディレクトリを作成しようとするとエラーがでます。
試しに、もう一度、mkdirメソッドを実行してみます。
エラーが出ました。
「ファイルは存在しています。」というエラーが出ていますね。
間違って、新しいディレクトリを作成して、前のディレクトリを消す心配がないので安心です。

なお、コード実行しても、ディレクトリが左のサイドバーに表示されない場合があります。
この場合は、左の上の方にある、回る矢印のマークをクリックしてみましょう。
このマークは、再読み込みのボタンです。
このボタンを押すと、現在のディレクトリを再読み込みくれるので、作成、削除したディレクトリやファイルを反映させることができます。

shutil.copy('./copy_from/000.png', './copy_to/000.png')
'./copy_to/0.png'

それでは、作成したディレクトリ「copy_to」に、ファイルをコピーしましょう。
ファイルのコピーには、shutilのcopyメソッドを使用します。
copyメソッドの使い方は、shutil.copyと書いて丸括弧。
丸括弧の中の1つ目の引数に、コピー元となるファイルパス、2つ目の引数にコピー先のファイルパスを記述します。
今回はディレクトリ「copy_from」の中から「000.png」のファイルを、ディレクトリ「copy_to」へ移動させます。
実行します。
ファイルがコピーされています。
ちなみに、shutilのcopyメソッドを実行すると、コピー先のファイルパスが表示されます。

shutil.copy('./copy_from/001.png', './copy_to')

なお、第二引数がディレクトリだけの場合、つまり、ディレクトリパスの場合、同じファイル名でコピーされます。
001.pngというファイルをコピーしてみましょう。
コピーできました。

shutil.copy('./copy_from/000.png', './copy_to/aaa.png')

また、第二引数のファイル名を変えて、別名で保存することもできます。
ファイル「002.png」を「aaa.png」で保存してみましょう。
実行します。
別名でコピーできました。

ファイルのコピー その2

shutil.copy2('./copy_from/000.png', './copy_to/000.png')
'./copy_to/0.png'

もう一つのファイルのcopyメソッドをご紹介します。
shutilのcopyメソッドは、メタデータはコピーされません。
メタデータとは、ファイルの作成日などのデータのことです。
普通、マウスでコピー、貼り付けを行うとメタデータはコピーされます。
このメタデータをコピーしてくれる、shutilのcopy2というメソッドをご紹介します。
使い方は、先ほどのshutilのcopyメソッドと全く同じです。
メソッド名をcopyからcopy2にするだけで良いです。
実行します。
こちらのメソッドも実行すると、コピー先のディレクトリ名とファイル名が表示されます。

ちなみに、先ほども説明した通り、マウス操作でのコピーではメタデータはコピーされます。
したがって、ファイルのコピーには、必要に応じて、copyメソッド、copy2メソッドを使用するとよいでしょう。

なお、copyメソッドもcopy2メソッドも、コピー先に同じファイル名があった場合、上書きされます。
使用する際は、大事なデータを上書きして消してしまわないように注意しましょう。

ディレクトリにあるすべてのファイル名の取得

read_path = './copy_from'

続いて、ディレクトリの中にある、全てのファイル名を取得します。
ファイル名を取得するには、まず、読み取るディレクトリパスを指定する必要があります。
読み込むディレクトリパスなので、変数名はread_pathとします。
パス名は、シングルクォーテーション、もしくはダブルクォーテーションで括って変数に代入する必要があります。
今回は見た目がスッキリするので、シングルクォーテーションを使っていきます。
変数「read_path」に、読み取るディレクトリパスである'./copy_from'というディレクトリパスを代入します。

read_files = os.listdir(read_path)

それでは、ディレクトリ「copy_from」の中にある全てのファイル名を取得します。
ディレクトリの全てのファイル名を取得するには、osのリストディアメソッドを使用します。
このメソッドを実行すると、そのディレクトリにあるファイル名を取得して、リスト型にしてくれます。
メソッドの引数には、読み取りたいディレクトリのパスを渡します。
したがって、os.listdir、読み取りたいディレクトリパスが入った、read_pathを渡します。
これをread_files
実行します。

print(read_files)
['.DS_Store', '0.png', '1.png', '10.png', '11.png', '12.png', '13.png', '14.png', '15.png', '16.png', '17.png', '18.png', '19.png', '2.png', '20.png', '21.png', '22.png', '23.png', '24.png', '25.png', '26.png', '27.png', '28.png', '29.png', '3.png', '30.png', '31.png', '32.png', '33.png', '34.png', '35.png', '36.png', '37.png', '38.png', '39.png', '4.png', '40.png', '41.png', '42.png', '43.png', '44.png', '45.png', '46.png', '47.png', '48.png', '49.png', '5.png', '50.png', '51.png', '52.png', '53.png', '54.png', '55.png', '56.png', '57.png', '58.png', '59.png', '6.png', '60.png', '61.png', '62.png', '63.png', '64.png', '65.png', '66.png', '67.png', '68.png', '69.png', '7.png', '70.png', '71.png', '72.png', '73.png', '74.png', '75.png', '76.png', '77.png', '78.png', '79.png', '8.png', '80.png', '81.png', '82.png', '83.png', '84.png', '85.png', '86.png', '87.png', '88.png', '89.png', '9.png', '90.png', '91.png', '92.png', '93.png', '94.png', '95.png', '96.png', '97.png', '98.png', '99.png', '株式会社A.csv', '株式会社A.xlsx', '株式会社B.csv', '株式会社B.xlsx', '株式会社C.csv', '株式会社C.xlsx', '株式会社D.csv', '株式会社D.xlsx', '株式会社E.csv', '株式会社E.xlsx', '株式会社F.csv', '株式会社F.xlsx', '株式会社G.csv', '株式会社G.xlsx', '株式会社H.csv', '株式会社H.xlsx', '株式会社I.csv', '株式会社I.xlsx', '株式会社J.csv', '株式会社J.xlsx', '株式会社K.csv', '株式会社K.xlsx', '株式会社L.csv', '株式会社L.xlsx', '株式会社M.csv', '株式会社M.xlsx', '株式会社N.csv', '株式会社N.xlsx', '株式会社O.csv', '株式会社O.xlsx', '株式会社P.csv', '株式会社P.xlsx', '株式会社Q.csv', '株式会社Q.xlsx', '株式会社R.csv', '株式会社R.xlsx', '株式会社S.csv', '株式会社S.xlsx', '株式会社T.csv', '株式会社T.xlsx', '株式会社U.csv', '株式会社U.xlsx', '株式会社V.csv', '株式会社V.xlsx', '株式会社W.csv', '株式会社W.xlsx', '株式会社X.csv', '株式会社X.xlsx', '株式会社Y.csv', '株式会社Y.xlsx', '株式会社Z.csv', '株式会社Z.xlsx']

変数read_filesを表示させてみましょう。
ファイル名を取得できているようです。

read_files[:5]
['.DS_Store', '0.png', '1.png', '10.png', '11.png']

Python超入門コースで解説しましたが、リストの上位5件を取得するにはスライスを使用します。
実行します。
上位5件を取得できました。
サクッとリストの中身を確認したいときなどに使えます。

for read_name in read_files[:5]:
    print(read_name)
.DS_Store
0.png
1.png
10.png
11.png

それでは、ディレクトリ「copy_from」の全てのファイルをディレクトリ「copy_to」にコピーします。
取得した全てのファイル名に対して、shutilのcopy2メソッドを使用するプログラムを書きます。
kinocodeの「python超入門コース、反復」で、ご紹介していますが、for文でリストの中身を参照することができます。
ここは、少しわかりくいと思いますので、実際のコードでみていきましょう。
フォーと書いて、アイ、イン、リスト名を記述します。
for 変数名をかきましょう。 in リスト名を書きましょう。今回は、read_filesとなります。
今回は上位5件を表示させてみましょう。
リストの中身は、forの次の変数、今回だとread_nameに代入されています。
リストの中身が取得できているか、printで出力させてみてみましょう。
インテンドをあけてprint(read_name)と書きます。
実行します。
リストの中身が1つずつ参照できているのが確認できます。

for read_name in read_files:
    copy_from_path = './copy_from/' + read_name
    copy_to_path = './copy_to/' + read_name 
    shutil.copy2(copy_from_path, copy_to_path)

それでは、先ほどの、shutil.copy2メソッドと合わせて、全てのファイルをコピーしていきましょう。
for文は先ほどと同じです。
コピー元になるファイルパスを、original_pathという変数に代入します。
original_pathと書いて、イコール、コピー元になるディレクトリを書きます。
そして、リストの1つ1つの要素が入った、変数read_nameを、プラスで文字列として結合します。
リストの中身のファイルは、copy_fromの下の階層にあるで、copy_fromの最後にスラッシュを入れています。
続いて、コピー先のファイルパスを、copy_to_pathという変数に代入します。
パスの書き方は、先ほどのoriginal_pathと同じでです。ディレクトリがcopy_fromから、copy_toに変わるので、そこだけ変えて書きます。
そして、shutil.copy2を書きます。
1つ目の引数に、コピー元である、copy_from_pathを書きます。
2つ目の引数に、コピー先である、copy_to_pathを書きます。
実行します。

フォルダの中を確認してみましょう。
全てのファイルがコピーできました!

ディレクトリのコピー

shutil.copytree('./copy_from', './copytree_to')
'./copytree_to'

ここまで、for文を使用した、ファイルのコピー方法を紹介しました。
ここで、1行で今までの説明したことをできるメソッドをご紹介します。
それは、shutil.copytreeメソッドです。
このcopyメソッドとの違いがあります。

copyメソッドは、ファイルをコピーするメソッドです。
copytreeメソッドは、ディレクトリをコピーするメソッドです。
したがって、copytreeメソッドは個別のファイルを指定してコピーすることができません。

他にも、copytreeメソッドは、新しいディレクトリを自動で作成し、その中にあるファイルをコピーをします。
そのため、ディレクトリがすでに存在する場合はエラーになる。
また、copytreeメソッドは、メタデータもコピーしてくれます。

copytreeメソッドの使い方をみていきましょう。
使い方は、ご紹介してきた、copyメソッドとほぼ同じです。
1つ目の引数に、コピー元のディレクトリパスを指定します。
2つ目の引数に、コピー先のディレクトリパスを指定します。
実行してみましょう。

コピーできているか確認をしてみましょう。
コピーできているようです。

ファイルの移動

os.mkdir('./move_to')

次に、ファイルを移動するメソッドを紹介します。
ファイルの移動には、shutilのmoveメソッドを使用します。
moveメソッドは、すでにあるディレクトリにファイルを移動させるメソッドです。
ですので、ここで再度、移動させるディレクトリを、mkdirメソッドで作成しましょう。
os.mkdir、丸括弧、作成するディレクトリは、move_toとします。

shutil.move('./copy_from/000.png', './move_to/000.png')
'./move_to/0.png'

shutilのmoveメソッドも、copyメソッドとほぼ同じ使い方です。
1つ目の引数に、移動元のファイルパスを指定します。
2つ目の引数に、移動先のファイルパスを指定します。
早速、フォルダ「copy_from」から、「000.png」というファイルを現在のディレクトリに移動させます。
shutil.move、丸括弧、1つ目の引数に、「./copy_from/0.png」と書きます。
2つ目の引数に、、「./move_to/0.png」とします。
実行します。
move_toのフォルダに、「0.png」が移動してきました。

shutil.move('./move_to/000.png', './copy_from/000.png')
'./copy_from/0.png'

さて、今から、copy_fromからmove_toの全てのファイルを元のフォルダ「copy_from」に戻しておきましょう。
先程のコードの引数を逆にするだけでオッケーです。
実行します。

for read_name in read_files:
    copy_from_path = './copy_from/' + read_name
    move_to_path = './move_to/' + read_name 
    shutil.move(copy_from_path, move_to_path)

それでは、全てのファイルをmove_toから移動させます。
先程のコピーの方法とほぼ同じです。
ディレクトリ名を変更し、copy2と書いていた部分をmoveに変更するだけです。

for read_name in read_files:
    copy_from_path = './copy_from/' + read_name
    move_to_path = './move_to/' + read_name
    shutil.move(move_to_path, copy_from_path)

移動したファイルを元に戻しておきましょう。
moveメソッドの引数を交換するだけです。
実行します。
移動できました。
簡単ですよね。

ファイルの種類ごとにファイル名を取得

read_path = './copy_from'
file_list = os.listdir(read_path)

次に、拡張子ごとに、ファイル名を取得する方法についてご紹介します。
まずは、一番最初にご紹介した、osのlistdirメソッドで、指定のディレクトリの全てのファイル名を取得します。
ディレクトリ「copy_from」のすべてのファイル名を取得します。
取得したファイル名をread_filesという変数に代入します。
実行します。

file_name='kinocode.csv'
file_name.endswith('.csv')
True

さて、リストである「read_files」に代入されたファイル名が指定した拡張子なのか、そうではないのか判定したいと思います。
判定をするには、endswithを使用します。
このメソッドは、文字列の最後の文字が、指定した文字列がどうかを判定してくれます。
今回は、csvファイルなのかそうではないのか判定をしてみましょう。
いきなりfile_listでやるのではなく、変数file_nameに、'kinocode.csv'という文字列を代入して実験してみます。
この'kinocode.csv'がリストの要素に入っていると仮定します。
これをendswithメソッドを使って判定をします。
endswithメソッドの使い方は、判定したい変数や文字列にドットを書き、エンズウィスと書いて、丸括弧。丸括弧の中に文字列の最後に含まれている文字列を渡します。
今回は.csvで終わっている文字列であるか判定したいので.csvを書きます。
実行します。
そうするとTrueが返ってきました。
つまり、指定した文字列で終わる場合はTrue、終わらない場合はFlaseが返ってきます。

file_name='kinocode.csv'
file_name.endswith('.png')
False

endswithメソッドで.pngであるか判定をしてみましょう。
実行します。
Flaseが返ってきました。
変数「file_name」には.csvで終わる文字列が代入されているので、endswithメソッドの動きは正しいですよね。

#「.csv」の拡張子だけのファイル名をリストを作る
file_list = []
file_extension = '.csv'

for read_name in read_files:
    if read_name.endswith(file_extension):
        file_list.append(read_name)

続いて、for文とif文を組み合わせて、csvファイルのみを取得してみましょう。
まず、csvのファイル名を保存する、リストを作成します。
リストの作成は、変数名、イコール、角かっこです。
ここでは、file_listという変数を使用します。
続いて、拡張子を指定します。
csvのファイルを読み取りたいので、file_extensionという変数に、シングルクォーテーションで括った、ドット、csvを代入します。
続いて、for文です。
リストを参照する、for文を書きます。
参照するリスト名が、先程作成したread_filesです。

続いてfor文の中で、ファイル名が、csvファイルかどうか判定するif文を書きます。

for文の中にifを書いて、read_name.endswithと書いて、括弧、file_extensionを渡します。
最後にコロンを書きます。
このif文では、最後に、ドット、csvが含まれていた時に、if文に書かれている処理が実行します。
ですので、if文の中には、csvのファイル名を保存するために作成したリストに、appendメソッドで、ファイル名を追加していきます。
file_listと書いて、ドット、append、括弧、引数に、ファイル名であるread_nameを記述します。
実行します。
これで変数「read_list」に.csvで終わる文字列のみのリストとなっているはずです。

print(file_list)
['株式会社A.csv', '株式会社B.csv', '株式会社C.csv', '株式会社D.csv', '株式会社E.csv', '株式会社F.csv', '株式会社G.csv', '株式会社H.csv', '株式会社I.csv', '株式会社J.csv', '株式会社K.csv', '株式会社L.csv', '株式会社M.csv', '株式会社N.csv', '株式会社O.csv', '株式会社P.csv', '株式会社Q.csv', '株式会社R.csv', '株式会社S.csv', '株式会社T.csv', '株式会社U.csv', '株式会社V.csv', '株式会社W.csv', '株式会社X.csv', '株式会社Y.csv', '株式会社Z.csv']

確認してみましょう。
csvファイルのみのリストになっています。

ファイルの削除

os.remove('./copy_to/000.png')

今まではファイルをコピーする方法、移動する方法をみてきました。
次に、ファイルの削除方法を説明します。
ディレクトリ「copy_to」にある"000.png"のファイルを削除してみましょう。
削除の方法は、簡単です。
os.removeと書いて削除したいファイルパスを記述します。
実行します。
ファイルが削除されました。

read_path='./copy_to'
read_files = os.listdir(read_path)

for read_name in read_files:
    copy_to_path = read_path + '/' + read_name
    os.remove(copy_to_path)

紹介したremoveメソッドをfor文と組み合わせてファイルを一括削除していきます。
ディレクトリ「copy_to」にあるファイルを削除していきましょう。
先ほどコピーしたファイルパスを次々と引数に渡すことで、ファイルを削除していくことができます。
実行します。
ファイルが削除できています。

フォルダの削除

os.rmdir('./copy_to')

次にディレクトを削除する方法をみていきます。
read_nameを削除するにはrmdirメソッドを使います。
使い方は、os.rmdirと書いて丸括弧。丸括弧の中に削除したいread_nameを記述すれば良いです。
read_name「copy_to」を削除してみましょう。
実行します。
削除されました。

os.rmdir('./move_to')
os.rmdir('./copytree_to')
---------------------------------------------------------------------------

OSError                                   Traceback (most recent call last)

 in 
      1 os.rmdir('./move_to')
----> 2 os.rmdir('./copytree_to')

OSError: [Errno 39] Directory not empty: './copytree_to'

ちなみに、rmdirメソッドは、ディレクトリの中が空でなければ削除することができません。
ディレクトリ「move_to」と「copytree_to」を削除してみましょう。
実行します。
ディレクトリ「move_to」は削除されましたが、「copytree_to」は削除されず、エラーになりました。
ディレクトリが空ではありませんというエラーメッセージです。

shutil.rmtree('./copytree_to')

ディレクトリが空じゃなくても削除するにはshutil.rmtreeメソッドを使います。
やってみましょう。
実行します。
削除されました。

ファイルの種類ごとのフォルダを作成して、種類ごとのフォルダにわける

for i in [1,2,3]:
    for j in [4,5,6]:
        print(i,"-",j)
1 - 4
1 - 5
1 - 6
2 - 4
2 - 5
2 - 6
3 - 4
3 - 5
3 - 6

さて、最後に、タイトルにある通り「ファイルの種類ごとのフォルダを作成して、種類ごとのフォルダにわけていく」ということをやっていきたいと思います。
少しソースコードは長くなりますが、安心してください。
今までのレッスンでお教えしたことを、ブロックのように組み合わせるだけです。
プログラミングは、1つ1つの処理を組み合わせていけば複雑な処理もできるようになります。
ここの部分で、そういったこともご理解いただけると思うので、それも意識しながらご覧ください。

振り分ける方法は、for文を使います。
もう少し厳密にいうと、2重のfor文を使います。
2重のfor文とは、for文の中にfor文があるというものです。
ここが少しわかりにくいため、コードを使って説明をします。

外側のfor文は1,2,3を回します。
リストの要素はiに代入されていきます。
内側のfor文は4,5,6を回します。
リストの要素はjに代入されていきます。

そして、繰り返し処理がどのように回っているのかiとjを表示させてみましょう。

これはどういう動きになるかというと、まず最初に外側のループが回ります。
1がiに代入されます。
次に、内側のループにいき、4がjに代入されます。
そうするとまず1-4が表示されます。
次に、内側の2周目が回ります。
1-5となります。
内側の3周目が回り、1-6。
内側が周り切ったら、外側の2周目にいき、2-4,2-5,2-6。
外側2周目で内側が周り切ったら、外側3周目にいき3-4,3-5,3-6となります。
このように2重ループは、内側が周り切ったら外側が回るという仕組みです。

実行して確認してみましょう。
予想通りの結果となりました。

file_extensions = ['csv', 'png', 'xlsx']
file_extensions 

#読み取りたいディレクトリパスを入れる
read_path = './copy_from'
read_files = os.listdir(read_path)

#拡張子リストを取得するフォー文を追加する。
for file_extension in file_extensions:
    os.mkdir('./' + file_extension)

    #指定の拡張子だけのファイル名をリストを作る
    file_list = []
    for file_name in read_files:
        if file_name.endswith(file_extension):
            file_list.append(file_name)

    #read_listから、指定の拡張子のファイルだけ指定のディレクトリにコピーする
    for file_name in file_list:
        copy_from = './' + read_path + '/' + file_name    
        copy_to = './' + file_extension + '/' + file_name 
        shutil.copy2(copy_from, copy_to)

さて、実際に「ファイルの種類ごとのフォルダを作成して、種類ごとのフォルダにわけていく」ということをやっていきたいと思います。
タイトルは「ファイルの種類ごとのフォルダを作成して、種類ごとのフォルダにわけていく」ですが、今のみなさんであれば、「拡張子ごとのディレクトリを作成して、拡張子ごとのディレクトリにわけていく」という言葉でわかりますよね。

まず、拡張子ごとのディレクトリを作成し、拡張子ごとにディレクトリを振り分けていきたいと思いますので、拡張子の名前リストを作成します。
file_extensionsという変数に、拡張子の名前のリストを代入します。
今回は、jpg,csv、png、xlsxのリストとします。

次に、読み取りたいディレクトリを記述して読み取ります。
読み取りたいディレクトリは、「copy_from」なので、read_pathに代入して、そのディレクトリにあるファイルを読み取り、read_filesに代入します。

さて、それでは、拡張子ごとのディレクトリを作成し、拡張子ごとにディレクトリを振り分けていきます。
振り分ける方法は、先程説明したように2重for文を使います。

最初に、拡張子の名前が代入されたリストの要素を参照するfor文を書きます。
拡張子のリストが入っているのはfile_extensionsでした。
これをfor文で1つずつ取得して、file_extensionに代入をします
次に、拡張子ごとのディレクトリを作成するため、mkdirを使います。
引数で、現在階層と、拡張子の要素が入った、変数、file_extensionに代入をしますを結合して渡します。
この処理によって、拡張子の名前のディレクトリが作成されます。

なお、最初に説明したように、mkdirメソッドはディレクトリが存在しているとエラーがでます。
同じディレクトリ名がある場合は削除しておきましょう。

後のコードは、先ほど作成したcsvファイルの読み取りコード、コピーのコードとほぼ同じです。
ここは、ファイル名のリストを作るのは今までみてきたのと同じです。
外側1周目、つまり、file_extensionの要素の1つ目、つまり、csvのときです。
ファイル名がcsvのものだけ変数「file_list」に追加されていきます。

次に、そのcsvで終わるファイル名のみ、変数「file_list」に代入されているので、それをfor文で1つ1つ取り出し、file_nameの変数に代入します。
これも今ままでみてきたとおりです。
1つ違うとするなら、コピー元のディレクトリ名がread_pathに代入されているので記述し、コピー先のディレクトリ名がfile_extensionに代入しているのでここに記述をします。
あとは一緒です。

これで拡張子ごとにファイルを振り分けられるはずです。
実行します。

各拡張子ごとのディレクトリが作成されました。
中身を確認します。
拡張子ごとにファイルが振り分けられていますね。

shutil.rmtree('./xlsx')
shutil.rmtree('./png')
shutil.rmtree('./csv')

最後に復習です。
ファイルが入っているディレクトリはどうやって消すでしょうか?
そうです。rmtreeでしたよね。
全部消しておきましょう。
実行します。

そうすると、レッスンを始めた時と同じ状態に戻りました。