python - simple - Como importar um arquivo de texto no AWS S3 para o pandas sem gravar no disco




using s3 to store images (4)

Eu tenho um arquivo de texto salvo no S3, que é uma tabela delimitada por tabulação. Quero carregá-lo no pandas, mas não consigo salvá-lo primeiro porque estou executando em um servidor heroku. Aqui está o que eu tenho até agora.

import io
import boto3
import os
import pandas as pd

os.environ["AWS_ACCESS_KEY_ID"] = "xxxxxxxx"
os.environ["AWS_SECRET_ACCESS_KEY"] = "xxxxxxxx"

s3_client = boto3.client('s3')
response = s3_client.get_object(Bucket="my_bucket",Key="filename.txt")
file = response["Body"]


pd.read_csv(file, header=14, delimiter="\t", low_memory=False)

o erro é

OSError: Expected file path name or file-like object, got <class 'bytes'> type

Como faço para converter o corpo da resposta em um formato aceito pelos pandas?

pd.read_csv(io.StringIO(file), header=14, delimiter="\t", low_memory=False)

returns

TypeError: initial_value must be str or None, not StreamingBody

pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)

returns

TypeError: 'StreamingBody' does not support the buffer interface

ATUALIZAÇÃO - Usando o seguinte trabalhou

file = response["Body"].read()

e

pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)


Agora os pandas podem lidar com URLs S3 . Você poderia simplesmente fazer:

import pandas as pd
import s3fs

df = pd.read_csv('s3://bucket-name/file.csv')

Você precisa instalar o s3fs se não o tiver. pip install s3fs

Autenticação

Se o seu bucket S3 for particular e exigir autenticação, você terá duas opções:

1- Adicione credenciais de acesso ao seu arquivo de configuração ~/.aws/credentials

[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Ou

2- Defina as seguintes variáveis ​​de ambiente com seus valores adequados:

  • aws_access_key_id
  • aws_secret_access_key
  • aws_session_token

Como os arquivos podem ser muito grandes, não é aconselhável carregá-los no dataframe completamente. Portanto, leia linha por linha e salve-o no quadro de dados. Sim, também podemos fornecer o tamanho do pedaço no read_csv, mas precisamos manter o número de linhas lidas.

Por isso, eu vim com essa engenharia:

def create_file_object_for_streaming(self):
        print("creating file object for streaming")
        self.file_object = self.bucket.Object(key=self.package_s3_key)
        print("File object is: " + str(self.file_object))
        print("Object file created.")
        return self.file_object

for row in codecs.getreader(self.encoding)(self.response[u'Body']).readlines():
            row_string = StringIO(row)
            df = pd.read_csv(row_string, sep=",")

Também excluo o df quando o trabalho estiver concluído. del df


Uma opção é converter o csv em json via df.to_dict() e depois armazená-lo como uma string. Observe que isso é relevante apenas se o CSV não for um requisito, mas você deseja colocar rapidamente o quadro de dados em um bucket S3 e recuperá-lo novamente.

from boto.s3.connection import S3Connection
import pandas as pd
import yaml

conn = S3Connection()
mybucket = conn.get_bucket('mybucketName')
myKey = mybucket.get_key("myKeyName")

myKey.set_contents_from_string(str(df.to_dict()))

Isso converterá o df em uma cadeia de caracteres dict e, em seguida, salve-o como json no S3. Mais tarde, você pode lê-lo no mesmo formato json:

df = pd.DataFrame(yaml.load(myKey.get_contents_as_string()))

As outras soluções também são boas, mas isso é um pouco mais simples. O Yaml pode não ser necessariamente necessário, mas você precisa de algo para analisar a string json. Se o arquivo S3 não precisar necessariamente ser um CSV, isso pode ser uma solução rápida.





boto3