Cloudinary のアセット一覧をPythonでCSV出力してみた
Guten Tag, ベルリンより伊藤です。
Cloudinaryの画像一覧やURL一覧をCSVで出力できないか?という問いについて、要望はチラホラあるようですが、現時点でコンソールやAPIでそのまま取得する機能は提供されていません。
Admin API の Get Resources で JSON 形式で一覧を取得することができるので、簡単なスクリプトを書いてCSV出力をやってみました。
◆ 参考:Cloudinary: How do I browse through all the resources in my account using the API?
Python の場合
Pythonで実装しました。Cloudinary API のクレデンシャル設定など基本的なところは過去のブログをご参考ください。
画像一覧を取得する
Get Resources の概要は次の通り:
- 何もオプションを指定しないと、画像アセットのみを最大10件出力
resource_type
オプションは image/video/raw から指定(デフォルトは image)max_results
オプションで、1リクエストで最大500件まで結果を取得するよう指定できる(デフォルトは10件)- 結果が最大件数を超える場合、JSONのレスポンスに
next_cursor
という次のページのIDのような値が含まれる
注記 本稿の例では、配信タイプが upload/private/authenticated のいずれかのみの環境で検証しています。fetch、facebook、text 等ではレスポンスに本稿の記載とは異なるキーを持つ可能性があり、それらが混在すると正しく結果取得できない場合があります。(2020年9月10日 追記)
ここでは、結果にこの next_cursor
のキーが含まれる限り処理を繰り返すとしています。
# -*- coding:utf-8 -*- import cloudinary import pandas as pd cloudinary.config( cloud_name = 'クラウド名', api_key = 'APIキー', api_secret = 'シークレット' ) res = cloudinary.api.resources(max_results = 500) df = pd.json_normalize(res['resources']) df.to_csv('./export.csv', index=False) while 'next_cursor' in res: res = cloudinary.api.resources(max_results = 500, next_cursor = res['next_cursor']) df = pd.json_normalize(res['resources']) df.to_csv('./export.csv', mode='a', header=False, index=False)
こんな感じで出力することができました。
asset_id,public_id,format,version,resource_type,type,created_at,bytes,width,height,access_mode,url,secure_url b409166171ae281e6b9476fb703dc8e9,test/stairs,jpg,1598530919,image,upload,2020-08-27T12:21:59Z,493498,1529,2040,authenticated,http://res.cloudinary.com/CLOUD_NAME/image/upload/s--wvi-tlEu--/v1598530919/test/stairs.jpg,https://res.cloudinary.com/CLOUD_NAME/image/upload/s--wvi-tlEu--/v1598530919/test/stairs.jpg 24ffd9776a737b04222d7301cfc29972,items/upa6it2sfs7sanb4sx0n,jpg,1598283014,image,upload,2020-08-24T15:30:14Z,54205,1100,1100,public,http://res.cloudinary.com/CLOUD_NAME/image/upload/v1598283014/items/upa6it2sfs7sanb4sx0n.jpg,https://res.cloudinary.com/CLOUD_NAME/image/upload/v1598283014/items/upa6it2sfs7sanb4sx0n.jpg
すべてのアセット一覧を取得する
[2020年9月10日 更新] upload/private/authenticated で、画像と動画とそれ以外のアセットをまとめて出力する方法を追記しました。
画像と動画のレスポンスは同様の値を持ちますが、raw(画像・動画以外)のレスポンスには 'format', 'width', 'height' の列がありません。以下、rawのみの出力例です。
asset_id,public_id,version,resource_type,type,created_at,bytes,access_mode,url,secure_url 5e8156b10301d93ce4556218d27dccf8,caption.vtt,1575568128,raw,upload,2019-12-05T17:48:48Z,92,public,http://res.cloudinary.com/CLOUD_NAME/raw/upload/v1575568128/caption.vtt,https://res.cloudinary.com/CLOUD_NAME/raw/upload/v1575568128/caption.vtt
raw の場合、不足するこれらの列には値に "-" が入るようにし、すべて(画像、動画、それ以外)のアセット一覧を一つのファイルに出力する処理を以下で行ないます。
また、ここでは配信タイプに明示的に upload/private/authenticated を指定しており、fetch 等の他の配信タイプのアセットは取得されません。
# ここでimportとクレデンシャル re_types = ['image', 'video', 'raw'] dlv_types = ['upload', 'private', 'authenticated'] output_path = './export.csv' num = 0 def get_resources(num, re_type, dlv_type): res = cloudinary.api.resources( resource_type = re_type, type = dlv_type, max_results = 500) if num == 0: # 初回だけヘッダありで新規出力/上書き write_new_csv(res) else: # 2回目以降はヘッダなしで追記 write_add_csv(res, re_type) while 'next_cursor' in res: res = cloudinary.api.resources( resource_type = re_type, type = dlv_type, max_results = 500, next_cursor = res['next_cursor']) write_add_csv(res, re_type) def write_new_csv(res): df = pd.json_normalize(res['resources']) df.to_csv(output_path, index=False) def write_add_csv(res, re_type): df = pd.json_normalize(res['resources']) if re_type == re_types[2] and not df.empty: # rawで、かつ値がある場合 df.insert(2, 'format', '-') df.insert(8, 'width', '-') df.insert(9, 'height', '-') df.to_csv(output_path, mode='a', header=False, index=False) for re_type in re_types: for dlv_type in dlv_types: get_resources(num, re_type, dlv_type) num += 1
参考
- Pythonで辞書のキー・値の存在を確認、取得(検索) | note.nkmk.me
- pandasでcsvファイルの書き出し・追記(to_csv) | note.nkmk.me
- PythonでRESAS APIを使ってデータをダウンロード | note.nkmk.me
- Python配列のループ処理 - Qiita
Node.js の場合
REPL で画像一覧を取得する
冒頭の Cloudinary の記事内コメントを参考に Node.js も簡単に試してみました。こちらはより触り慣れていないので対話型での実装で、非同期処理までは動作確認できていません。。
冒頭のクレデンシャル等は過去のブログを参照してください。
var result = []; var options = { max_results: 500, resource_type: 'image' }; function listResources(next_cursor) { if(next_cursor) { options["next_cursor"] = next_cursor; // next_cursorがあればオプションに加える } cloudinary.api.resources(options, function(error, res){ resources = res.resources; result = result.concat(resources); var more = res.next_cursor; if (more) { // next_cursorがあれば指定して繰り返す、なければundefinedで終了 listResources(more); } }); } listResources(); const createCsvWriter = require('csv-writer').createObjectCsvWriter; const csvWriter = createCsvWriter({ path: './output.csv', header: [ // 必要な列を記述していく {id: 'public_id', title: 'public_id'}, {id: 'format', title: 'format'}, {id: 'type', title: 'type'}, {id: 'created_at', title: 'created_at'}, {id: 'bytes', title: 'bytes'}, {id: 'width', title: 'width'}, {id: 'height', title: 'height'}, {id: 'secure_url', title: 'secure_url'}] }); csvWriter.writeRecords(result).then(() => { console.log('done'); });
出力例は以下の通りです。
public_id,format,type,created_at,bytes,width,height,secure_url test/stairs,jpg,upload,2020-08-27T12:21:59Z,493498,1529,2040,https://res.cloudinary.com/CLOUD_NAME/image/upload/s--wvi-tlEu--/v1598530919/test/stairs.jpg items/upa6it2sfs7sanb4sx0n,jpg,upload,2020-08-24T15:30:14Z,54205,1100,1100,https://res.cloudinary.com/CLOUD_NAME/image/upload/v1598283014/items/upa6it2sfs7sanb4sx0n.jpg
スクリプトファイルで画像一覧を取得する
[2020年9月8日 更新] 非同期処理で動作確認できたので、追記しました。
非同期処理というものを勉強して実装させたのがこちらです。
const cloudinary = require('cloudinary').v2; const createCsvWriter = require('csv-writer').createObjectCsvWriter; cloudinary.config({ cloud_name: 'クラウド名', api_key: 'APIキー', api_secret: 'シークレット' }); var result = []; var options = { max_results: 500, resource_type: 'image' }; const listResources = (next_cursor) => { return new Promise((resolve) => { if(next_cursor) { options["next_cursor"] = next_cursor; } cloudinary.api.resources(options, function(error, res){ resources = res.resources; result = result.concat(resources); var more = res.next_cursor; if (more) { listResources(more).then(() => { resolve(result); }); } else { resolve(); } }) }) } const csvWriter = createCsvWriter({ path: './output.csv', header: [ // 必要な列を記述(※前項と同様のため抜粋) {id: 'public_id', title: 'public_id'} ] }); listResources().then((result) => { csvWriter.writeRecords(result); }).then(() => { console.log('done'); }).catch((e) => { console.log('err: '+e); })
参考
後半のCSV出力部分は1つ目の参考サイト様のコピペでございます。非同期処理での再帰呼び出しは2つ目のサイト様をかなり参考にさせていただきました。ありがとうございます。
- [Node.js] JSONをCSV形式でファイルに書き込む - csv-writer
- NodejsのPromiseで再帰呼び出しを行う!JavaScriptの非同期処理がおもしろい。 | しずかなかずし
- Node.jsの非同期処理のコールバック地獄を Promise、Generator、async_await を使って解決する - Qiita
- Node.js 非同期処理・超入門 -- Promiseとasync/await - Qiita
- Promiseを使う - JavaScript | MDN
注意点
Admin API はプランごとに定められたリクエストレート上限があります。無料試用アカウントなら1時間あたり500リクエストです。アカウントの各種上限値は、設定画面の右パネルから確認できます。
クラスメソッドはCloudinaryのパートナーとして導入のお手伝いをさせていただいています。お気軽にこちらからお問い合わせください。こちらから無料でもサインアップいただけます。