【Tips】boto3のS3.ListObjectsで末尾のオブジェクト情報を一発で取得する

こんにちは。DA部の春田です。

表題の件について、Pythonのイテレータを使用した強力な手法を簡単にご紹介します。

問題

S3のオブジェクト情報を取得するのに、ListObjectsオペレーションはよく使用されるかと思います。AWS CLIの場合はaws s3 ls、Pythonのboto3の場合はclient.list_objects_v2()などが代表的ですね。

ただ、これらのオペレーションはオブジェクト名の昇順で値が返されるため、オブジェクト数が数万件もあると「末尾のオブジェクト情報だけでいいのに、一番上から取得してるの、なんて無駄なのかしら」と思っていた方いらっしゃるのではないでしょうか?

パラメータのPrefixやStartAfterで事前の絞り込みはできますが、取得順序を制御することはできませんし、boto3の場合1000件を超えるとContinuationTokenが必要になるので、ループ処理を書くのが面倒臭かったり。

解決策

そんな時は、resource('s3').Bucket('hoge').objects.all()でイテレータを生成し、標準ライブラリのcollections.deque()でイテレータの末尾を取得します。具体的には以下の通りです。

import boto3
from collections import deque

# S3バケットhoge配下のオブジェクト一覧を取得するイテレータを生成
objects_iter = boto3.resource('s3').Bucket('hoge').objects.all()
# イテレータの最後のオブジェクトを吐き出して、キーを取得する
key = deque(objects_iter, maxlen=1).pop().key
print('S3 tail object: {}'.format(key))

resource('s3').Bucket('hoge').objects.all()で生成される list(s3.ObjectSummary) はイテレータであり、呼び出されるタイミングでリクエストをかける仕様になっています。そのため、生成したイテレータの末尾にアクセスできれば、1回のリクエストで末尾のオブジェクト情報を取得することができます。

イテレータの末尾にアクセスするには、標準ライブラリcollectionsdequeオブジェクトを使用します。maxlenパラメータはUNIXでいうtailコマンドのような役割も兼ねており、これを使用して長さ1、末尾データのみのdequeオブジェクトを作成したところで、pop()してs3.ObjectSummaryにアクセスします。

応用例として、例えばresource('s3').Bucket('hoge').objects.filter()を使用すれば、様々な条件を指定した上でオブジェクト情報を取得することもできます。また、iter(deque(objects_iter, maxlen=100))とすれば、末尾100件までのイテレータを生成し直すことができます。この方法は、itertoolsの拡張版パッケージmore-itertoolstailメソッドとして実装されているので、こちらを使用するのもアリです。

API Reference — more-itertools 8.5.0 documentation

イテレータを扱う際dequeオブジェクトはかなり便利なので、ぜひ一度公式ドキュメントも見てみてください。

collections --- コンテナデータ型 — Python 3.8.5 ドキュメント

参考