如何在AWS S3上将文本文件导入pandas而无需写入磁盘

132

我在S3上保存了一个制表符分隔的文本文件,需要将其加载到pandas中,但由于我正在Heroku服务器上运行,无法先保存该文件。以下是我目前的进展。

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)

错误信息是:
OSError: Expected file path name or file-like object, got <class 'bytes'> type

如何将响应体转换为 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

更新 - 使用以下内容有效

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

and

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

尝试使用io.BytesIO(file)io.StringIO(file)代替read_csv()调用中的file - MaxU - stand with Ukraine
你可以像这个答案中所示使用io.StringIO - IanS
这些建议都没有起作用。你可以在我的帖子编辑中看到错误信息。 - alpalalpal
1
更新部分对我起作用了。谢谢。 - Wim Berchmans
10个回答

168

pandas 使用 boto 来进行 read_csv,因此您应该能够:

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

如果您因为使用python3.4+而需要boto3,则可以

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

版本0.20.1 开始,pandas 使用 s3fs,请参阅下面的答案


有没有办法在不公开URL的情况下使用它?文件需要保持私密。 - alpalalpal
boto3文档展示了如何配置身份验证来访问私有文件:http://boto3.readthedocs.io/en/latest/guide/quickstart.html - Stefan
1
它抛出了NoCredentialsError。我该如何为它设置S3凭据?我是Python和Boto的新手。 - Sunil Rao
17
在最后一个使用boto3的示例中,我发现我需要执行以下操作:`df = pd.read_csv(io.BytesIO(obj['Body'].read()), encoding='utf8')`该代码的作用是从S3对象中读取.csv格式的数据,并将其存储在名为'df'的Pandas DataFrame对象中。其中,'obj'是通过boto3库获取的S3对象,'io.BytesIO'将二进制数据转换成可读写的文件流,'encoding'参数指定了读取数据时所使用的字符编码类型为'utf8'。 - user394430
1
这个答案已经过时了。请参考Wesam的答案 - gerrit
1
@Stefan 非常感谢,我已经寻找了好几天如何将S3中的csv简单读入pandas dataframe,这个方法对我很有用!(使用Python 3.6) - Chris

119

现在pandas可以处理S3 URL了。你只需要简单地执行:

import pandas as pd
import s3fs

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

如果您还没有安装 s3fs,您需要先进行安装pip install s3fs

认证

如果您的 S3 存储桶是私有的并且需要进行认证,您可以有两个选项:

1- 将访问凭据添加到您的 ~/.aws/credentials 配置文件中

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

或者

2- 使用正确的值设置以下环境变量:

  • aws_access_key_id
  • aws_secret_access_key
  • aws_session_token

1
漂亮。适用于Python3。 - Kyler Brown
认证怎么样? - James Wierzba
2
@JamesWierzba,我在上面的答案中添加了更多有关身份验证的细节。 - Sam
3
处理多个AWS配置文件时,你如何选择要使用的配置文件?s3fs有profile_name选项,但我不确定在pandas中如何使用。 - Ivo Merchiers
1
@IanS 目前不是这样的,我首先使用指定的配置文件通过s3fs打开文件对象,然后再像这里一样使用pandas读取它 https://github.com/pandas-dev/pandas/issues/16692 - Ivo Merchiers
显示剩余3条评论

22

6
记住,S3的URL同样被支持,但需要安装S3Fs库来处理。 - Julio Villane
什么关于身份验证? - James Wierzba
除非URL是公开的,否则带有身份验证的URL可能会很困难。不确定简单/基本的HTTP身份验证是否有效。 - Raveen Beemsingh
关于身份验证,您需要在您的机器上设置credentials文件,即您需要设置您的AWS CLI。 - undefined

19

针对 Python 3.6+,Amazon 现在有一个非常好用的库,可以将 Pandas 与其服务一起使用,称为 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=['--'])

安装awswrangler:pip install awswrangler


4
如果您将使用AWS/Python/Pandas/Glue等技术... AWS Wrangler 将成为您的新好伴侣。 - Brian Wylie

13

使用 s3fs 可以按照以下方式完成:

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
我认为使用s3fs,你甚至可以写成 df = pd.read_csv('s3://mybucket/path/to/object/foo.pkl') - louis_guitton
1
@louis_guitton 看起来这个代码可以用 pd-read_csv 运行,但不能用 read_pickle。 - Sip

3

由于文件可能会非常大,将它们全部加载到数据框中是不明智的。因此,逐行读取并将其保存在数据框中。是的,我们也可以在read_csv中提供块大小,但是我们必须维护读取的行数。

因此,我想出了这个方案:

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

完成工作后,我也会删除df。

del df

2
对于文本文件,您可以使用以下代码,例如带有管道分隔符的文件:
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)

1
import os
import pandas as pd
import boto3

session = boto3.Session(profile_name="test")

os.environ['AWS_ACCESS_KEY_ID'] = session.get_credentials().access_key
os.environ['AWS_SECRET_ACCESS_KEY'] = session.get_credentials().secret_key

这样,您就可以使用存储在~/.aws/credentials中的任何配置文件(AWS帐户)。

df = pd.read_csv("s3://xxxx.csv")

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

2
请在您的代码中添加一些解释。 - andrey.shedko

0
一种选项是通过 df.to_dict() 将 csv 转换为 json,然后将其存储为字符串。请注意,这仅在 CSV 不是必需项,但您只想快速将数据框放入 S3 存储桶中并再次检索它时才相关。
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()))

这将把df转换为字典字符串,然后将其保存为S3中的json格式。您可以以相同的json格式稍后读取它:

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

其他解决方案也不错,但这个更简单一些。Yaml 可能不是必需的,但你需要某种东西来解析 JSON 字符串。如果 S3 文件不一定需要是 CSV,那么这可能是一个快速修复。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接