So importieren Sie eine Textdatei in AWS S3 in Pandas, ohne auf die Festplatte zu schreiben

89

Ich habe eine Textdatei in S3 gespeichert, die eine tabulatorgetrennte Tabelle ist. Ich möchte es in Pandas laden, kann es aber nicht zuerst speichern, da ich auf einem Heroku-Server laufe. Folgendes habe ich bisher.

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)

Der Fehler ist

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

Wie konvertiere ich den Antworttext in ein Format, das Pandas akzeptieren?

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

UPDATE - Die folgenden Funktionen haben funktioniert

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

und

pd.read_csv(io.BytesIO(file), header=14, delimiter="\t", low_memory=False)
alpalalpal
quelle
versuchen Sie es so: io.BytesIO(file)oder io.StringIO(file)statt fileim read_csv()Anruf
MaxU
Sie könnten io.StringIOwie in dieser Antwort verwenden .
IanS
Keiner dieser Vorschläge hat funktioniert. Sie können die Fehler in meinem Beitrag bearbeiten sehen.
Alpalalpal
1
Der UPDATE-Teil hat bei mir funktioniert. Vielen Dank.
Wim Berchmans

Antworten:

110

pandasverwendet botofür read_csv, so sollten Sie in der Lage sein:

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

Wenn Sie brauchen, boto3weil Sie eingeschaltet sind python3.4+, können Sie

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

Da Version 0.20.1 pandas verwendet s3fs, siehe Antwort unten.

Stefan
quelle
Gibt es eine Möglichkeit, eine URL zu verwenden, ohne sie für alle öffentlich zu machen? Die Datei muss privat bleiben.
Alpalalpal
Die boto3Dokumente zeigen, wie die Authentifizierung so konfiguriert wird, dass Sie auch auf private Dateien zugreifen können: boto3.readthedocs.io/en/latest/guide/quickstart.html
Stefan
1
Es wird NoCredentialsError ausgelöst. Wie setze ich s3-Anmeldeinformationen dafür? Ich bin neu in Python und Boto
Sunil Rao
15
Ich stellte fest, dass ich im letzten Beispiel mit boto3 Folgendes tun musste: df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')
user394430
Diese Antwort ist veraltet . Bitte sehen Sie Wesams Antwort .
Gerrit
79

Jetzt können Pandas mit S3-URLs umgehen . Sie könnten einfach tun:

import pandas as pd
import s3fs

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

Sie müssen installieren,s3fs wenn Sie es nicht haben. pip install s3fs

Authentifizierung

Wenn Ihr S3-Bucket privat ist und eine Authentifizierung erfordert, haben Sie zwei Möglichkeiten:

1- Fügen Sie Ihrer Konfigurationsdatei Zugriffsdaten hinzu~/.aws/credentials

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

Oder

2- Stellen Sie die folgenden Umgebungsvariablen mit ihren richtigen Werten ein:

  • aws_access_key_id
  • aws_secret_access_key
  • aws_session_token
Wesam
quelle
Wunderschönen. Funktioniert in Python3.
Kyler Brown
Wie wäre es mit Authentifizierung ..?
James Wierzba
1
@ JamesWierzba, ich habe meiner obigen Antwort weitere Details zur Authentifizierung hinzugefügt.
Wesam
3
Wie können Sie beim Umgang mit mehreren aws-Profilen auswählen, welches Profil verwendet werden soll? s3fs hat die Option profile_name, aber ich bin mir nicht sicher, wie das mit Pandas funktioniert.
Ivo Merchiers
1
@IanS Derzeit nicht wirklich, ich öffne das Dateiobjekt zuerst mit s3fs (unter Verwendung des angegebenen Profils) und lese es dann mit Pandas, wie hier github.com/pandas-dev/pandas/issues/16692
Ivo Merchiers
14

Dies wird jetzt in den neuesten Pandas unterstützt. Sehen

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

z.B.,

df = pd.read_csv('s3://pandas-test/tips.csv')
Raveen Beemsingh
quelle
4
Denken Sie daran, dass "S3-URLs ebenfalls behandelt werden, aber die S3F-Bibliothek installiert werden muss"
Julio Villane,
Was ist mit der Authentifizierung
James Wierzba
URL mit Auth kann schwierig sein, es sei denn, die URL wird als öffentlich angezeigt, nicht sicher, ob einfache / grundlegende http Auth funktioniert,
Raveen Beemsingh
9

Mit s3fs kann dies wie folgt durchgeführt werden:

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
quelle
2
Ich denke mit s3fs kann man sogar schreibendf = pd.read_csv('s3://mybucket/path/to/object/foo.pkl')
louis_guitton
1
@ Louis_guitton dies scheint mit pd-read_csv zu funktionieren, aber nicht mit read_pickle
Sip
1

Da die Dateien zu groß sein können, ist es nicht ratsam, sie insgesamt in den Datenrahmen zu laden. Lesen Sie daher Zeile für Zeile und speichern Sie es im Datenrahmen. Ja, wir können auch die Blockgröße in read_csv angeben, aber dann müssen wir die Anzahl der gelesenen Zeilen beibehalten.

Daher habe ich mir dieses Engineering ausgedacht:

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

Ich lösche auch die df, sobald die Arbeit erledigt ist. del df

aviral sanjay
quelle
1

Für Textdateien können Sie den folgenden Code mit einer durch Pipe getrennten Datei verwenden, zum Beispiel: -

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
quelle
0

Eine Option besteht darin, die CSV über über in json zu konvertieren df.to_dict()und sie dann als Zeichenfolge zu speichern. Beachten Sie, dass dies nur relevant ist, wenn die CSV nicht erforderlich ist, Sie den Datenrahmen jedoch schnell in einen S3-Bucket einfügen und erneut abrufen möchten.

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

Dadurch wird der df in einen diktierten String konvertiert und in S3 als json gespeichert. Sie können es später im gleichen JSON-Format lesen:

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

Die anderen Lösungen sind ebenfalls gut, aber das ist etwas einfacher. Yaml ist möglicherweise nicht unbedingt erforderlich, aber Sie benötigen etwas, um die JSON-Zeichenfolge zu analysieren. Wenn die S3 - Datei nicht unbedingt benötigt eine CSV zu sein , dies kann eine schnelle Lösung sein.

billmanH
quelle