smart_openでS3のオブジェクトをストリーミング処理してみる

2023.05.22

はじめに

データアナリティクス事業本部のkobayashiです。

S3で比較的大きなサイズのオブジェクトを扱う場合に扱うインスタンスに潤沢なメモリやストレージがあればそのままファイルを取り扱うことができますが、リソースが潤沢でない場合はストリーミングでアップロードやダウンロードした方がインスタンスのリソースを抑えることができ効率的です。

そのような処理をPythonで実現するのに良さげなライブラリがあったので試してみたのでまとめます

smart_openとは

smart_openとはS3、GCSをはじめとしたクラウドストレージやローカルファイル間で大きなファイルを扱う際に効率的にストリーミングを行いつつ、解凍・圧縮機能も行えるライブラリになります。Pythonの標準open()と互換性がありopen()で扱える機能は100%扱えます。

smart_openを使ってみる

はじめにsmart_openをインストールしますがこれはいつも通りpipでインストールできます。

pip install smart_open

これでsmart_openはインストールできたので以下を試してみたいと思います。

  • ローカル上のcsvファイルを1行ずつ読み込む
  • 読み込んだデータを1行ずつS3上のオブジェクトに書き込む
  • 作成したS3上のcsvを1行づつ読み込んでprintする

ソースファイルは以下の内容になります。

pref_cd,pref_name,pref_alphabet,population,population_male,population_female,population_of_2015,population_variation,population_var_ratio,area,population_density,age_average,age_median,under_15,between_15_64,over_65,under_15_ratio,bet_15_64_ratio,over_65_ratio,under_15_male,bet_15_64_male,over_65_male,under_15_ratio_male,bet_15_64_ratio_male,over_65_ratio_male,under_15_female,bet_15_64_female,over_65_female,under_15_ratio_female,bet_15_64_ratio_female,over_65_ratio_female,sex_ratio,japanese,foreigners,households,households_general,households_institutional,households_2015,households_nuclear,households_couples,households_couples_child,households_male_child,households_female_child,households_single,households_single_over65,households_couples_over_65_60_reposted,households_3gen_reposted
01,北海道,Hokkaido,5224614,2465088,2759526,5381733,-157119,-2.91949,83424.44,66.6,49.78238,51.34284,556526,2988800,1679288,10.652,57.20614,32.14186,284897,1477750,702441,11.55728,59.94715,28.49558,271629,1511050,976847,9.84332,54.75759,35.39909,89.33012,5188441,36173,2476846,2469063,7783,2444810,1324406,584819,511571,29921,198095,999825,361735,345741,59601
02,青森県,Aomori-ken,1237984,583402,654582,1308265,-70281,-5.37208,9645.64,128.3,50.82694,53.35318,130259,689910,417815,10.52186,55.72851,33.74963,66483,345000,171919,11.39574,59.1359,29.46836,63776,344910,245896,9.74301,52.69164,37.56535,89.12588,1232227,5757,511526,509649,1877,510945,268760,100962,109399,7734,50665,168917,71752,60995,42895
03,岩手県,Iwate-ken,1210534,582952,627582,1279594,-69060,-5.39702,15275.01,79.2,50.58634,52.86212,132735,670784,407015,10.965,55.41224,33.62276,67919,342813,172220,11.65087,58.80639,29.54274,64816,327971,234795,10.32789,52.25947,37.41264,92.88858,1203203,7331,492436,490828,1608,493049,252005,94910,105474,7549,44072,163290,62424,57656,46934
04,宮城県,Miyagi-ken,2301996,1122598,1179398,2333899,-31903,-1.36694,7282.29,316.1,47.42673,48.13367,268931,1385425,647640,11.68251,60.18364,28.13385,137916,701792,282890,12.28543,62.51499,25.19958,131015,683633,364750,11.10863,57.96457,30.92679,95.18398,2280203,21793,982523,980549,1974,944720,507063,181038,234787,13068,78170,362255,97239,102638,67505
05,秋田県,Akita-ken,959502,452439,507063,1023119,-63617,-6.21795,11637.52,82.4,52.89928,56.44966,92855,506960,359687,9.67742,52.83574,37.48684,47526,255856,149057,10.5044,56.55039,32.94521,45329,251104,210630,8.93952,49.52126,41.53922,89.22737,955659,3843,385187,383531,1656,388560,203177,81772,81097,5755,34553,117169,55437,52719,39453
....

早速ですが、以下がsmart_openライブラリを使ったコードは以下になります。

s3_streaming.py

from smart_open import open
import boto3
import os

session = boto3.Session(
    aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],
)

s3_path = 's3://{bucket_name}/sample_data/s3_streaming/streaming_test.csv'
lo_path = './major_results_2020.csv'

# ストリーミングアップロード
with open(url, 'wb', transport_params={'client': session.client('s3')}) as fout:
    with open(lo_path, 'rb') as f:
        for s_line in f:
            fout.write(s_line)

# ストリーミングダウンロード
for line in open(url, 'r', transport_params={'client': session.client('s3')}):
    print(repr(line))

コードを解説します。

  • 5-8行目: S3へアクセスするための認証情報を設定する
  • 14行目: smart_openのopenメソッドで認証情報を使ってs3上の対象パスを開く
  • 15-17行目: ローカルファイルを1行ずつ開きつつ1行づつS3上のオブジェクトに書き込む
  • 20-21行目: 3上のcsvを1行づつ読み込んでprintする

上記のスクリプトを実行するとS3上にstreaming_test.csvを作成し、その後streaming_test.csvの中身を1行ずつ出力できます。

$python3 s3_streaming.py 
b'\xef\xbb\xbfpref_cd,pref_name,pref_alphabet,population,population_male,population_female,population_of_2015,population_variation,population_var_ratio,area,population_density,age_average,age_median,under_15,between_15_64,over_65,under_15_ratio,bet_15_64_ratio,over_65_ratio,under_15_male,bet_15_64_male,over_65_male,under_15_ratio_male,bet_15_64_ratio_male,over_65_ratio_male,under_15_female,bet_15_64_female,over_65_female,under_15_ratio_female,bet_15_64_ratio_female,over_65_ratio_female,sex_ratio,japanese,foreigners,households,households_general,households_institutional,households_2015,households_nuclear,households_couples,households_couples_child,households_male_child,households_female_child,households_single,households_single_over65,households_couples_over_65_60_reposted,households_3gen_reposted\r\n'
b'01,\xe5\x8c\x97\xe6\xb5\xb7\xe9\x81\x93,Hokkaido,5224614,2465088,2759526,5381733,-157119,-2.91949,83424.44,66.6,49.78238,51.34284,556526,2988800,1679288,10.652,57.20614,32.14186,284897,1477750,702441,11.55728,59.94715,28.49558,271629,1511050,976847,9.84332,54.75759,35.39909,89.33012,5188441,36173,2476846,2469063,7783,2444810,1324406,584819,511571,29921,198095,999825,361735,345741,59601\r\n'
b'02,\xe9\x9d\x92\xe6\xa3\xae\xe7\x9c\x8c,Aomori-ken,1237984,583402,654582,1308265,-70281,-5.37208,9645.64,128.3,50.82694,53.35318,130259,689910,417815,10.52186,55.72851,33.74963,66483,345000,171919,11.39574,59.1359,29.46836,63776,344910,245896,9.74301,52.69164,37.56535,89.12588,1232227,5757,511526,509649,1877,510945,268760,100962,109399,7734,50665,168917,71752,60995,42895\r\n'

まとめ

S3で比較的大きなサイズのオブジェクトを扱う場合に扱う際に有益なライブラリであるsmart_openを使ってみました。簡単に使えるので、リソースが潤沢でない場合はストリーミングでアップロードやダウンロードする場合は積極的に使ってみると良いかと思います。

最後まで読んで頂いてありがとうございました。