写真素材note「空気感フォト」を定期的に公開しています。
JPGは3サイズ(L/M/S)あり、3サイズ分のZIPもあるので公開するまで手間がかかります。たとえば、
- Lightroom:僕カラーでエディット
- Lightroom:サイズごとに指定のファイル名で出力(No40-L-01.jpg/No40-S-06.jpg)
- ZIPANG:サイズごとにZIP圧縮
- Transmit:サーバにディレクトリを作り、JPGとZIPをアップロード
と画像データを用意するフロー「だけ」でこのくらいあります。さらに公開するまであと数手あります。
でね、単純作業や繰り返し作業は、ITを利用することで効率化が期待できる部分です。ということで、上記3〜4をPythonスクリプトを組んで半自動化することにしました。
動作環境
- macOS Mojave ver 10.14.15
- Python 3.7.3
ソースコード
""" My note project workflow script. Create 3 zip files from JPG files. Upload all JPG/ZIP files to FTPS server. """ from ftplib import FTP_TLS from pathlib import Path import zipfile WORK_DIR = '任意の作業ディレクトリ' FTP_SERVER = 'サーバーURL' FTP_USER = 'ユーザID' FTP_PASSWORD = 'パスワード' UPLOAD_PATH = 'アップロード先ディレクトリ' p_num = '' # Project number def get_file_lists(): """ Get compressed file lists. Returns: l_list: L-size jpg file list m_list: M s_list: S """ path = Path(WORK_DIR) l_list, m_list, s_list = [], [], [] for file_path in path.iterdir(): if file_path.suffix == '.jpg': if 'L' in file_path.name: l_list.append(str(file_path.name)) elif 'M' in file_path.name: m_list.append(str(file_path.name)) else: s_list.append(str(file_path.name)) return l_list, m_list, s_list def create_zip_files(): """ Create 3 zip files per L/M/S-size files. """ # Create targets list targets_list = [] l_list, m_list, s_list = get_file_lists() targets_list.append(l_list) targets_list.append(m_list) targets_list.append(s_list) # Get project number from L-size file name # Project number is 2nd & 3rd characters of file name # (ex. No40-L-01.jpg -> it is 40) num = l_list[0] global p_num p_num = num[2:4] # Create 3 zip files for i, size in enumerate(['L', 'M', 'S']): zip_file_path = WORK_DIR + f'No{p_num}-{size}.zip' print(f'Creation start: {zip_file_path}') with zipfile.ZipFile(zip_file_path, 'w', compression=zipfile.ZIP_DEFLATED) as new_zip: for file_name in targets_list[i]: new_zip.write(WORK_DIR + f'{file_name}', arcname=file_name) print(f'File combining: {file_name}') print('_____Creation complated_____') def ftps_upload_files(): """ Upload jpg/zip files to FTPS(Passive mode). """ # Get upload file list(jpg & zip) path = Path(WORK_DIR) upload_list = [p for p in path.iterdir() if p.suffix == '.jpg' or p.suffix == '.zip'] try: # Login FTP server ftps = FTP_TLS(FTP_SERVER) ftps.login(FTP_USER, FTP_PASSWORD) ftps.prot_p() ftps.set_pasv('true') print('login OK') # Make project number dir ftps.cwd(UPLOAD_PATH) global p_num p_num_dir = 'No' + p_num if p_num_dir in ftps.nlst('.'): print('This dir already exist.') else: ftps.mkd(p_num_dir) print('Create project dir.') ftps.cwd(p_num_dir) print(f'Upload directory: {ftps.pwd()}') # Upload files count = 1 total_count = len(upload_list) for upload_file in upload_list: with open(upload_file, 'rb') as f: ftps.storbinary('STOR ' + upload_file.name, f) print(f'Uploading({count}/{total_count}): {upload_file}') count += 1 except ftps.all_error as e: print(e) print('_____Upload failed_____') else: print('_____Upload complated_____') finally: ftps.quit() print('_____Close connection_____') if __name__ == '__main__': create_zip_files() ftps_upload_files()
処理をざっくり説明すると
- 作業ディレクトリにあるJPGファイルを走査し、L/M/Sサイズごとのパスリストを作る
- パスリストから「NoXX-L-01.jpg」の数字部分を取得(5で使う)
- パスリストからL/M/SそれぞれのサイズのZIPファイルを作る
- FTPサーバにログイン
- note用のディレクトリに「NoXX」のディレクトリを作る
- JPG、ZIPをアップロードする
PythonはUdemyのこちらの講座で学んでいて、コードやコメントの書き方を真似ています。
今回はユニットテストしていませんが、上記講座でpytestを学んだばかりなので、テストはやってみたいところ。Exceptionの確認だってしてないからね。
学んだことメモ
Pythonでファイル操作(pathlib)
ファイル操作でよく使われるのは「osモジュール」ですが、オブジェクトとしてパスを扱える「pathlibモジュール」を使いました。
例. 指定ディレクトリの拡張子がJPGであるファイルだけのリストを作る
from pathlib import Path path = Path(任意のディレクトリ) list = [] for file_path in path.iterdir(): if file_path.suffix == '.jpg': list.append(str(file_path.name))
カレントディレクトリを変えるなど、osモジュールでしかできないこともあるので、用途によって使い分けるのでしょう。
PythonでZIPファイルを作る(zipfile)
with構文でやるのが一般的ですね。
例. リストにあるファイルをhogehoge.zipにまとめる
import zipfile zip_file_path = WORK_DIR + 'hogehoge.zip' with zipfile.ZipFile(zip_file_path, 'w', compression=zipfile.ZIP_DEFLATED) as z: for file_name in file_list: z.write(WORK_DIR + f'{file_name}', arcname=file_name)
zip_file_pathはWORK_DIRを含めて絶対パスにしているので、z.write内で「arcname」にパスを除いたファイル名を指定しています。
PythonでFTPアップロード(ftplib)
あっさりとモジュールがある便利な時代。僕が使っているロリポップに合わせてPASVなどをPythonで書きます。
例. リストにあるファイルをFTPS、PASVモードでアップロード
from ftplib import FTP_TLS ftps = FTP_TLS(FTP_SERVER) ftps.login(FTP_USER, FTP_PASSWORD) ftps.prot_p() ftps.set_pasv('true') ftps.cwd(UPLOAD_PATH) # 任意のディレクトリへ for upload_file in upload_list: with open(upload_file, 'rb') as f: ftps.storbinary('STOR ' + upload_file.name, f) ftps.quit()
upload_file.nameはパスの末尾(ファイル名)を指すものです。
おわりに
Udemyでosモジュールを学んだのに、pathlibを使うという未知に踏み出したおかげで学べることがたくさんありました。そのほか、はじめて知ったenumrate関数やリスト内包表記を使ったりもしました。
Pythonって処理を上から下に単純に組むこともできます。でも、Pythonの機能を知っている/知らないではコード量も見栄えも違いますね。
少しでもPythonicなコードが書けていたら嬉しいな。
今回のコードが正解かはわかりません。もっとスマートに書けるかもしれないけど、今の僕が知っている(覚えている)ことで組みました。
このスクリプトには次の写真素材noteから活躍してもらいます。あ〜楽しかった!