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


98

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 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 que os pandas aceitarão?

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 funcionou

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

e

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

tente desta forma: io.BytesIO(file)ou em io.StringIO(file)vez de filena read_csv()chamada
MaxU

Você poderia usar io.StringIOcomo nesta resposta .
IanS

Nenhuma dessas sugestões funcionou. Você pode ver os erros na minha edição de postagem.
alpalalpal

1
A parte UPDATE funcionou para mim. Obrigado.
Wim Berchmans

Respostas:


116

pandasusa botopara read_csv, então você deve ser capaz de:

import boto
data = pd.read_csv('s3://bucket....csv')

Se você precisa boto3porque está ligado python3.4+, você pode

import boto3
import io
s3 = boto3.client('s3')
obj = s3.get_object(Bucket='bucket', Key='key')
df = pd.read_csv(io.BytesIO(obj['Body'].read()))

Desde que usa a versão 0.20.1 , veja a resposta abaixo.pandass3fs


Existe uma maneira de usar um URL sem torná-lo público para todos? O arquivo precisa permanecer privado.
alpalalpal

Os boto3documentos mostram como configurar a autenticação para que você também possa acessar arquivos privados: boto3.readthedocs.io/en/latest/guide/quickstart.html
Stefan

1
Ele está lançando NoCredentialsError. Como faço para definir credenciais s3 para ele? Sou novo em python e boto
Sunil Rao

15
Descobri que precisava fazer o seguinte no último exemplo com boto3: df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
user394430

Esta resposta está desatualizada . Por favor, veja a resposta de Wesams .
gerrit

85

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 instalars3fs se ainda não tiver. pip install s3fs

Autenticação

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

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

[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

Bonito. Funciona em python3.
Kyler Brown

que tal autenticação ..?
James Wierzba

1
@JamesWierzba, adicionei mais detalhes sobre autenticação à minha resposta acima.
Wesam

3
Ao lidar com vários perfis de aws, como você pode selecionar qual perfil deve ser usado? s3fs tem a opção profile_name, mas não tenho certeza de como isso funciona com o pandas.
Ivo Merchiers de

1
@IanS Na verdade não, atualmente, eu primeiro abro o objeto de arquivo com s3fs (usando o perfil especificado) e depois o leio com o pandas, como eles fazem aqui github.com/pandas-dev/pandas/issues/16692
Ivo Merchiers

16

Isso agora é compatível com os pandas mais recentes. Vejo

http://pandas.pydata.org/pandas-docs/stable/io.html#reading-remote-files

por exemplo.,

df = pd.read_csv('s3://pandas-test/tips.csv')

4
Lembre-se de que 'URLs S3 também são manipulados, mas exigem a instalação da biblioteca S3Fs'
Julio Villane

que tal autenticar
James Wierzba

url com autenticação pode ser difícil, a menos que o url seja exposto como público, não tenho certeza se a autenticação http simples / básica funcionará,
Raveen Beemsingh

10

Com s3fs, isso pode ser feito da seguinte maneira:

import s3fs
import pandas as pd
fs = s3fs.S3FileSystem(anon=False)

# CSV
with fs.open('mybucket/path/to/object/foo.pkl') as f:
    df = pd.read_csv(f)

# Pickle
with fs.open('mybucket/path/to/object/foo.pkl') as f:
    df = pd.read_pickle(f)

2
Acho que com o s3fs você pode até escreverdf = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
louis_guitton

1
@louis_guitton isso parece funcionar com pd-read_csv, mas não com read_pickle
Sip

2

Como os arquivos podem ser muito grandes, não é aconselhável carregá-los no dataframe juntos. Portanto, leia linha por linha e salve-o no dataframe. Sim, também podemos fornecer o tamanho do bloco no read_csv, mas então temos que manter o número de linhas lidas.

Portanto, eu vim com esta 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=",")

Eu também excluo o df quando o trabalho é concluído. del df


1

Para arquivos de texto, você pode usar o código abaixo com arquivo delimitado por barra vertical, por exemplo: -

import pandas as pd
import io
import boto3
s3_client = boto3.client('s3', use_ssl=False)
bucket = #
prefix = #
obj = s3_client.get_object(Bucket=bucket, Key=prefix+ filename)
df = pd.read_fwf((io.BytesIO(obj['Body'].read())) , encoding= 'unicode_escape', delimiter='|', error_bad_lines=False,header=None, dtype=str)

0

Uma opção é converter o csv em json via df.to_dict()e armazená-lo como uma string. Observe que isso só é relevante se o CSV não for um requisito, mas você apenas deseja colocar rapidamente o dataframe em um depósito 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 irá converter o df em uma string de dicionário e salvá-la como json no S3. Você pode lê-lo mais tarde 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. Yaml pode não ser necessariamente obrigatório, mas você precisa de algo para analisar a string json. Se o arquivo S3 não precisa necessariamente ser um CSV, isso pode ser uma solução rápida.


0

Para python 3.6+, a Amazon agora tem uma biblioteca muito boa para usar o Pandas com seus serviços, chamada awswrangler .

import awswrangler as wr
import boto3


# Boto3 session
session = boto3.session.Session(aws_access_key_id='XXXX', 
                                aws_secret_access_key='XXXX')

# Awswrangler pass forward all pd.read_csv() function args
df = wr.s3.read_csv(path='s3://bucket/path/',
                    boto3_session=session,
                    skiprows=2,
                    sep=';',
                    decimal=',',
                    na_values=['--'])

Para instalar o awswrangler: pip install awswrangler


-1
import s3fs
import pandas as pd
s3 = s3fs.S3FileSystem(profile='<profile_name>')
pd.read_csv(s3.open(<s3_path>))

1
Adicione alguma explicação ao seu código.
andrey.shedko
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.