Comment importer un fichier texte sur AWS S3 dans des pandas sans écrire sur le disque

91

J'ai un fichier texte enregistré sur S3 qui est une table délimitée par des tabulations. Je veux le charger dans des pandas mais je ne peux pas le sauvegarder d'abord car je suis en cours d'exécution sur un serveur heroku. Voici ce que j'ai jusqu'ici.

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)

l'erreur est

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

Comment convertir le corps de la réponse dans un format que les pandas accepteront?

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

MISE À JOUR - Utilisation de ce qui suit travaillé

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

et

pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)
alpalalpal
la source
essayez-le de cette façon: io.BytesIO(file)ou io.StringIO(file)au lieu de filel' read_csv()appel
MaxU
Vous pouvez utiliser io.StringIOcomme dans cette réponse .
IanS
Aucune de ces suggestions n'a fonctionné. Vous pouvez voir les erreurs dans ma publication.
alpalalpal
1
La partie UPDATE a fonctionné pour moi. Merci.
Wim Berchmans

Réponses:

110

pandasutilise botopour read_csv, vous devriez donc pouvoir:

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

Si vous avez besoin boto3parce que vous êtes allumé python3.4+, vous pouvez

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()))

Depuis la version 0.20.1 pandas utilise s3fs, voir la réponse ci-dessous.

Stefan
la source
Existe-t-il un moyen d'utiliser une URL sans la rendre publique à tout le monde? Le fichier doit rester privé.
alpalalpal
La boto3documentation montre comment configurer l'authentification afin que vous puissiez également accéder aux fichiers privés: boto3.readthedocs.io/en/latest/guide/quickstart.html
Stefan
1
Il lance NoCredentialsError. Comment puis-je définir les informations d'identification s3? Je suis nouveau sur python et boto
Sunil Rao
15
J'ai trouvé que je devais faire ce qui suit sur le dernier exemple avec boto3: df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
user394430
Cette réponse est périmée . Veuillez voir la réponse de Wesams .
gerrit le
80

Désormais, les pandas peuvent gérer les URL S3 . Vous pouvez simplement faire:

import pandas as pd
import s3fs

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

Vous devez installers3fs si vous ne l'avez pas. pip install s3fs

Authentification

Si votre compartiment S3 est privé et nécessite une authentification, vous avez deux options:

1- Ajoutez les identifiants d'accès à votre ~/.aws/credentialsfichier de configuration

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

Ou

2- Définissez les variables d'environnement suivantes avec leurs valeurs appropriées:

  • aws_access_key_id
  • aws_secret_access_key
  • aws_session_token
Wesam
la source
Magnifique. Fonctionne en python3.
Kyler Brown
que diriez-vous de l'authentification ..?
James Wierzba
1
@JamesWierzba, j'ai ajouté plus de détails sur l'authentification à ma réponse ci-dessus.
Wesam
3
Lorsque vous traitez avec plusieurs profils aws, comment pouvez-vous sélectionner le profil à utiliser? s3fs a l'option profile_name, mais je ne sais pas comment cela fonctionne avec les pandas.
Ivo Merchiers
1
@IanS Pas vraiment, actuellement, j'ouvre d'abord l'objet fichier avec s3fs (en utilisant le profil spécifié) puis je le lis avec des pandas, comme ils le font ici github.com/pandas-dev/pandas/issues/16692
Ivo Merchiers
15

Ceci est maintenant pris en charge dans les derniers pandas. Voir

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

par exemple.,

df = pd.read_csv('s3://pandas-test/tips.csv')
Raveen Beemsingh
la source
4
Rappelez-vous `` Les URL S3 sont également gérées mais nécessitent l'installation de la bibliothèque S3Fs ''
Julio Villane
Qu'en est-il de l'authentification
James Wierzba
url avec auth peut être difficile à moins que l'url ne soit exposée comme publique, je ne sais pas si l'authentification http simple / basique fonctionnera,
Raveen Beemsingh
9

Avec s3fs, cela peut être fait comme suit:

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)
Dror
la source
2
Je pense qu'avec s3fs, vous pouvez même écriredf = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
louis_guitton
1
@louis_guitton cela semble fonctionner avec pd-read_csv mais pas avec read_pickle
Sip le
1

Étant donné que les fichiers peuvent être trop volumineux, il n'est pas judicieux de les charger complètement dans le dataframe. Par conséquent, lisez ligne par ligne et enregistrez-le dans le dataframe. Oui, nous pouvons également fournir la taille du bloc dans read_csv, mais nous devons ensuite maintenir le nombre de lignes lues.

Par conséquent, j'ai proposé cette ingénierie:

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=",")

Je supprime également le df une fois le travail terminé. del df

aviral sanjay
la source
1

Pour les fichiers texte, vous pouvez utiliser le code ci-dessous avec un fichier délimité par des barres verticales par exemple: -

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)
Harry_pb
la source
0

Une option consiste à convertir le csv en json via df.to_dict(), puis à le stocker sous forme de chaîne. Notez que cela n'est pertinent que si le CSV n'est pas une exigence, mais que vous souhaitez simplement mettre rapidement le dataframe dans un compartiment S3 et le récupérer à nouveau.

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()))

Cela convertira le df en une chaîne de dict, puis l'enregistrera en tant que json dans S3. Vous pourrez le lire plus tard dans le même format json:

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

Les autres solutions sont également bonnes, mais c'est un peu plus simple. Yaml n'est pas forcément nécessaire, mais vous avez besoin de quelque chose pour analyser la chaîne json. Si le fichier S3 n'a pas nécessairement besoin d'être un CSV, cela peut être une solution rapide.

billmanH
la source