AWS Glue Crawlerでカンマを含むCSVを読み込む

この記事は[BeeX Advent Calendar 2024](https://qiita.com/advent-calendar/2024/beex)の3日目の記事です。

# はじめに
以下の記事のコメント欄にカンマを含むCSVを読み込む方法が書いてあったので試してみました。

https://qiita.com/kmats@github/items/e320f3fbdd9b4b5cbe45

# 概要
CSV等のファイルをS3に格納して、Glueを利用してメタデータをクローリングし、ETLを行って長期保管用のS3に格納するような処理を実装するケースがあると思います。
その中でメタデータをクローリングする際、意外にファイルの内容次第でうまく読み込めなかったりするケースがあります。
今回はカンマを区切り文字とするCSVファイル内の文字列にカンマが含まれている場合に、メタデータを想定通りクローリングする方法を検証したものになります。

# 作業内容
## 事象確認
まずは検証用のテストファイルを作ります。
普通はあり得ないと思いますが、以下のようなユーザー一覧CSVファイルがあるとします。
favoriteカラムには好きな果物を入れていますが、人によっては複数あるのでカラム区切りで設定されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/9b59bda1-dbe2-aed8-58d9-ac66bdb25eea.png)

テストファイルをS3バケットにアップロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/4b2b4172-9a6a-a2e9-7155-08fce54bafcd.png)

Glueコンソールからデータベースを手動で作成しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/ba52ed05-1f41-fe7e-2720-8c4f6b0d3281.png)

先ほどテストファイルをアップロードしたS3バケットを指定するようにGlue Crawlerを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/19539f4c-18a7-d61a-f196-2ce8a0ac9730.png)

作成したらCrawlerを実行して正常終了することを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/71720e8b-f05a-13d0-f846-32c364e61d74.png)

先ほど作成したデータベース配下にテーブルが自動で作成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/7f029550-5e04-682a-7611-dbb3d37456a4.png)

テーブルの詳細を見るとSchemaがおかしいことが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/c3755caf-0473-008a-94d5-1c76d01f804d.png)

試しにAthenaでクエリを投げてみると、「peach,orange」の部分のカンマが区切り文字として判定されてカラムが増えています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/eae0d1ca-fadc-c74e-308f-908dc24e012a.png)

このようにデータにカンマ等の区切り文字が含まれる場合、想定外の処理が行われる可能性があります。

## ダブルクォーテーションを付けた場合(NGのケース)
次もあまりないケースかと思いますが、カンマが含まれる文字列だけをダブルクォーテーションで囲った場合はどうなるでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/7c2c9519-f393-0cdb-97be-293c7f4b45d8.png)

設定項目は先ほどと同じですので、S3への上書きアップロードしてGlue Crawlerを実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/2f6fe255-48f2-b8cf-d44f-e56279b05062.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/63302dae-ad2e-da1d-6030-ab794c9cf67a.png)

Glue テーブルを見るとSchemaが4つに変わっているので、スキーマの判定は想定通りになってそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/11610eca-7429-c167-65c9-315ce311dc32.png)

じゃあAthenaでクエリするとどうなるかというと、test-user4のfavoriteカラムの表示が「"peach」となっているので、「,orange"」の部分が表示されていないことがわかります。
スキーマはちゃんと判定されていても文字内のカラムは区切り文字として判定されてしまっていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/2cf5498e-4f62-8333-f495-32d10a6ca483.png)


CSVファイルのデータを利用してGlueテーブルを作成する場合、Open CSV SerDeかLazy Simple SerDeのライブラリを利用する必要があります。
このうち、データにダブルクォーテーションが含まれる場合はOpen CSV SerDeを利用する必要があります。

GlueテーブルのAdvanced propertiesを見るとどちらのSerDeを利用しているか確認ができます。
デフォルトではLazy Simple SerDeを利用するように設定されています。
これではダブルクォーテーションで囲まれた値は正しく認識されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/4315cda3-5c17-b274-e64b-553d87ca5150.png)

次はOpen CSV SerDeを利用するようにCrawlerを変更して取り込みしてみます。

## ダブルクォーテーションを付けた場合(OKのケース)
CSVファイルのデータに対してOpen CSV SerDeを利用したい場合はGlueのカスタム分類子の機能を利用します。
https://docs.aws.amazon.com/ja_jp/glue/latest/dg/custom-classifier.html#custom-classifier-csv

GlueコンソールからClassifiers(分類子)を新規作成して、Classifier typeをCSV、CSV SerdeをOpen CSV SerDeのみ設定して作成します。
他の項目はデフォルトで問題ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/acdc33c0-179e-d379-6f57-06864a86be7d.png)

次に最初に作成したCrawlerのEditから編集画面に遷移し、データソースと分類子の設定画面から作成した分類子を指定して保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/9b2dd756-805d-bd3d-96af-3821ad9a4281.png)

Crawlerに差分判定させるために同じファイルをアップロードして、Crawlerを実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/0a006afd-6a5a-b929-c20c-7787ddd809f9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/0e0274bb-ac80-75c4-cc66-0b40abb8e4b3.png)

Crawlerは正常終了しましたがAthenaからクエリすると先ほどと同じ結果となりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/db08d0b5-6378-b7fa-4899-71b594fe2559.png)

これはテーブルをみていただくとわかりますが、「Serde serialization lib」がLazy Simple SerDeのままになっていることが原因です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/bb27cf9c-ded2-fa3f-ad79-2d5767e46103.png)

テーブルの再作成をすることでOpen CSV SerDeに変更することもできますが、新しいCSVファイルをアップロードしてCrawlerを実行させることで簡単に変更が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/6e66b6d8-e7f6-853d-80bb-b69605fc4914.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/b67a7816-d799-fd5a-df9f-6f4400e5a294.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/88fa862f-bc9b-beee-f3d4-e445ad15ab0a.png)

先ほどは「"peach」としか出力されてませんでしたが、今度は「peach,orange」と出力されました。
追加したtest-user6のfavoriteカラムも正常に出力されてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/0c4ad4ec-11d5-fe62-7b8f-ae08ebd5e933.png)


# おわりに
このようにAWS Glue Crawlerでカンマを含むCSVを取り込む際は少し注意が必要になります。

※あるべきとしては、そもそもカンマのような処理に影響がある文字が入らないようにデータ連携元と調整するか、取り込み前にS3→Lambda等で事前に前処理を入れる方が良いとは思います

できることは知っていましたが、たまたま検証する機会があったので記事に書いてみました。
この記事がどなたかの参考になれば幸いです。

# 参考URL
https://qiita.com/kmats@github/items/e320f3fbdd9b4b5cbe45
https://dev.classmethod.jp/articles/double-quotation-csv-glue-crawler-athena/