python - para - que es boto3
Cómo importar un archivo de texto en AWS S3 en pandas sin escribir en el disco (6)
Tengo un archivo de texto guardado en S3 que es una tabla delimitada por tabulaciones. Quiero cargarlo en pandas pero no puedo guardarlo primero porque lo estoy ejecutando en un servidor heroku. Esto es lo que tengo hasta ahora.
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)
el error es
OSError: Expected file path name or file-like object, got <class ''bytes''> type
¿Cómo convierto el cuerpo de respuesta en un formato que los pandas aceptarán?
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
ACTUALIZACIÓN - Usando el siguiente trabajo
file = response["Body"].read()
y
pd.read_csv(io.BytesIO(file), header=14, delimiter="/t", low_memory=False)
Ahora los pandas pueden manejar URL S3 . Simplemente podrías hacer:
import pandas as pd
import s3fs
df = pd.read_csv(''s3://bucket-name/file.csv'')
Necesita instalar
s3fs
si no lo tiene.
pip install s3fs
Autenticación
Si su depósito S3 es privado y requiere autenticación, tiene dos opciones:
1- Agregue credenciales de acceso a su
archivo de configuración
~/.aws/credentials
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
O
2- Establezca las siguientes variables de entorno con sus valores adecuados:
-
aws_access_key_id
-
aws_secret_access_key
-
aws_session_token
Como los archivos pueden ser demasiado grandes, no es aconsejable cargarlos en el marco de datos por completo. Por lo tanto, lea línea por línea y guárdelo en el marco de datos. Sí, también podemos proporcionar el tamaño del fragmento en read_csv, pero luego tenemos que mantener el número de filas leídas.
Por lo tanto, se me ocurrió esta ingeniería:
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=",")
También elimino el df una vez que el trabajo está hecho.
del df
Con s3fs se puede hacer de la siguiente manera:
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)
Esto ahora es compatible con los últimos pandas. Ver
http://pandas.pydata.org/pandas-docs/stable/io.html#reading-remote-files
p.ej.,
df = pd.read_csv(''s3://pandas-test/tips.csv'')
Una opción es convertir el csv a json a través de
df.to_dict()
y luego almacenarlo como una cadena.
Tenga en cuenta que esto solo es relevante si el CSV no es un requisito, pero solo desea colocar rápidamente el marco de datos en un depósito S3 y recuperarlo nuevamente.
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()))
Esto convertirá el df en una cadena dict, y luego lo guardará como json en S3. Más tarde puede leerlo en el mismo formato json:
df = pd.DataFrame(yaml.load(myKey.get_contents_as_string()))
Las otras soluciones también son buenas, pero esto es un poco más simple. Puede que no se requiera necesariamente Yaml, pero necesita algo para analizar la cadena json. Si el archivo S3 no necesariamente necesita ser un CSV, esto puede ser una solución rápida.
pandas
usa
boto
para
read_csv
, por lo que debería poder:
import boto
data = pd.read_csv(''s3://bucket....csv'')
Si necesita
boto3
porque está en
python3.4+
, puede
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()))