0
0

More than 1 year has passed since last update.

AWS S3のCSV(Shift JIS)をLambda+pandasで読み込んでみた

Posted at

windowsユーザー経由で上がるファイルはsjisなことが多く、csvの文字コードがUTF-8じゃないが故に

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x83 in position 0

といったあるあるエラーをS3ファイル->Lambda pandasで解決してみました。

問題はCSVがUTF-8でないことはもちろんですが、もう1つはS3のレスがStreamingBodyなところ。
既存ファイルをUTF-8で上げなおしてくれなんて言えないのでシステムで読み込む方法を模索。
今回のケースでは、

pandas.read_csv(response, encoding="shift-jis")
とか
pandas.read_csv(response, encoding ="cp932")

と単純にぶっ込んでも同じエラーになりました。

そこで、

with codecs.open("file/to/path", "r", "Shift-JIS", "ignore") as file:
    df = pd.read_table(file, delimiter=",")
    print(df)

という感じの記事を見つけましたが、
codecs.open()はファイルパスを第1引数に要求しているので、今回のオブジェクトタイプでは使えません。
※ S3オブジェクトを一旦Lambdaのtmpにおいてpathを読み込ませたらワンチャン策として良いかもしれませんが面倒なのでやめました。

なので、以下のようにしてみると読み込めました。

import json
import os
import boto3
from lib import AwsUtils
import pandas
from io import StringIO

def lambda_handler(event, context):
    """ S3にアップロードされたファイル(sjis)を読む """

    # events
    file_name = event.get('file_name')
    # constants
    BUCKET_NAME = os.environ['BUCKET_NAME']

    # ファイルはshift_jisを想定
    s3_client = boto3.client('s3')
    s3_obj = s3_client.get_object(Bucket=self.BUCKET_NAME,Key=f'uploads/{file_name}')
    origin_file_data = s3_obj['Body'].read()
    
    # ↓ココ
    domain_file_data = pandas.read_csv(
        StringIO(origin_file_data.decode("shift_jis")),
        encoding='cp932'
    )
    return domain_file_data

pandasのread_csv()は path or Object Likeな何か を第1引数に要求しています。
初めに、受けとったオブジェクトをdecode("shift_jis")し、encoding='cp932'で特殊文字の読込みも対応するようにし、pathは渡せないのでdecodeしたオブジェクトをStringIO()でラッピングすることでread_csvに対応させました。

sjisとcp932の違いは下記記事が参考になると思います。
https://qiita.com/nukorea/items/73c2438b1bbc0035b3a5

同じエラーで困ってる方は参考にしてみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0