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
同じエラーで困ってる方は参考にしてみてください。