GlueのPython Shellジョブでファイルの文字コードを変更してみた

こんにちは。DI部の大高です。

突然ですが「AWS Glueと仲良くなりたい!」ということで、初心者ながら色々とGlueを触ってみたいと思います。これからGlueを触る方向けに参考になれば幸いです。

Glueのジョブは「Spark」ジョブと「Python Shell」ジョブの2タイプがありますが、今回はよりシンプルなジョブだと思われる「Python Shell」ジョブを使ってS3のファイルを処理してみました。

やりたいこと

以下のような流れを想定して、主にファイルの文字コード処理にフォーカスしてジョブを作ってみたいと思います。

データの流れ

  1. ユーザがExcelで作成したファイルをタブ区切りのファイルとして保存する(この時点での文字コードは"Shift_JIS")
  2. ユーザは保存したファイルをS3にアップロードする
  3. S3にアップロードされたファイルをGlueのジョブで読み込んで、文字コードを"UTF-8"にしてS3に保存する

データの準備

まずはデータを準備します。データは以下のようなExcelファイルを作成し、保存時に「テキスト (タブ区切り) (*.txt)」として保存します。また、今回はファイル名をSample.in.tsvとして保存しました。

No	Title	Price
1	劇場版 仮面ライダービルド Be The One	6192.1
2	平成仮面ライダー20作記念 仮面ライダー平成ジェネレーションズ FOREVER	7115.2
3	ビルド NEW WORLD 仮面ライダークローズ	9720.3

このファイルをS3にアップロードします。適当に作成したバケット内のフォルダに配置しておきます。

IAMロールの準備

データの準備ができたので、早速Python Shellジョブを作成したいところですが、その前に権限回りも準備しておきます。

以下の記事を参考にGlue用のIAMロールを準備しました。なお、今回は開発用エンドポイントは利用しないので、参考記事内のStep4と5は飛ばしています。

AWS Glue 実践入門 環境準備編(1):IAM権限周りの設定について

GlueのPython Shellジョブの作成

さて、準備が出来たのでPython Shellジョブを作成します。マネージメントコンソールからGlueの画面を開き「ジョブの追加」をクリックします。

ジョブには適宜名前を付けて、先程準備したIAMロールを指定します。また「Type」にはPython shellを指定し、「このジョブ実行」としてはユーザーが作成する新しいスクリプトを指定します。スクリプト保存先はデフォルトのままでも良いですが、ちょっと個人用にパスを変えています。

その他はデフォルトのままで「次へ」ボタンをクリックします。次の画面では接続情報の設定ができますが、今回は何も設定せずに保存してスクリプト編集画面へ移ります。

スクリプトの編集

さて、記述するPythonスクリプトですが以下のようなスクリプトになります。

import boto3
import sys
from awsglue.utils import getResolvedOptions

#SRC_BUCKET_NAME='sample-bucket'
#SRC_OBJECT_KEY_NAME='glue/Sample.in.tsv'
#SRC_FILE_ENCODING='shift_jis'

#DEST_BUCKET_NAME='sample-bucket'
#DEST_OBJECT_KEY_NAME='glue/Sample.out.tsv'

# ジョブパラメータの読み込み
args = getResolvedOptions(sys.argv, ['SRC_BUCKET_NAME', 'SRC_OBJECT_KEY_NAME', 'SRC_FILE_ENCODING', 'DEST_BUCKET_NAME', 'DEST_OBJECT_KEY_NAME'])
print('args: {0}'.format(args))

# S3 Service Resource 準備
s3 = boto3.resource('s3')

# ファイルをロード
src_obj = s3.Object(args['SRC_BUCKET_NAME'], args['SRC_OBJECT_KEY_NAME'])
body = src_obj.get()['Body'].read().decode(args['SRC_FILE_ENCODING'])

# ファイルを保存
dest_obj = s3.Object(args['DEST_BUCKET_NAME'], args['DEST_OBJECT_KEY_NAME'])
dest_obj.put(Body = body)

ジョブパラメータ

コメントアウトしている箇所は、ジョブパラメータの値の参考値です。ジョブ作成時にデフォルトのままにして飛ばしたのですが、Glueのジョブは「ジョブパラメータ」として「キー」と「値」でパラメータを指定できます。これは後でジョブの設定を変更して設定します。

指定したパラメータはgetResolvedOptionsを利用して読み込みます。ここの第二引数でちゃんと指定しないとargsには格納されないので注意します。また、確認のために値をprintで出力させて見ました。

getResolvedOptions を使用して、パラメータにアクセスする - AWS Glue

ファイルの取得と保存

S3へのアクセスはboto3を利用しています。

S3から読み込み先パスにあるファイルをs3.Objectを利用してsrc_objとして定義し、get()['Body'].read()で読み込んでいます。また、文字コードがShift_JISのファイルなのでdecodeで文字コードを指定して読み込みます。

これでUTF-8のデータになったので、同様にして保存先をdest_objとして定義して、put(Body = body)で保存しています。

スクリプトは以上のように単純なものになります。

ジョブパラメータを設定する

最後に、先程スクリプト内で利用したジョブパラメータを設定します。ジョブ一覧に戻り、ジョブにチェックを入れて「アクション」から「ジョブの編集」をクリックします。

ジョブの編集画面では真ん中ぐらいまでスクロールすると「セキュリティ設定、スクリプトライブラリおよびジョブパラメータ (省略可能)」という箇所があるので、開きます。

すると、更に下の方に「ジョブパラメータ」の設定箇所があるので、ここに「キー」と「値」を設定していきます。ここで一番注意が必要なのは キーの先頭に--が必要 ということです。始めはこれに気が付かなくて、ちょっとハマりました。

Pythonスクリプト内では、キーの先頭に--は不要ですが、ここの「キー」では必要なので注意して下さい。

いざ、実行!

これで準備万端です。ジョブを実行してみます。先程と同様にジョブ一覧からジョブにチェックを入れて「アクション」から「ジョブの実行」をクリックします。

すると、ジョブ実行ウィンドウが開くので「ジョブの実行」をクリックします。するとジョブ一覧の上部にジョブ実行中の旨が表示されます。

表示が消えたら再度ジョブを選択すると、以下のように表示されて「Succeeded」ステータスになっていることが確認できます。「ログ」もクリックして確認してみましょう。

スクリプトに記述した通り、ジョブパラメータが出力されていますね。

肝心のS3に保存したファイルも確認してみましょう。以下のとおり出力されています。

ファイルをダウンロードして確認したところ、文字コードもちゃんとUTF-8になっていました!

まとめ

まずはPython Shellジョブを触ってみました。こちらは単純に普段書くようなPythonコードとさほど変わらないので書きやすいですね。今後はここから少しずつ発展して、もっとGlueと仲良くなっていきたいと思います。

どなたかのお役に立てれば幸いです。それでは!