从私有S3存储桶读取文件到pandas数据帧

47
我正在尝试从私有S3存储桶中读取CSV文件到pandas数据帧中:
df = pandas.read_csv('s3://mybucket/file.csv')

我可以从公共存储桶中读取文件,但是从私有存储桶中读取文件会导致HTTP 403:禁止错误。

我已经使用aws configure配置了AWS凭证。

我可以使用使用AWS凭证的boto3从私有存储桶下载文件。看来我需要配置pandas以使用AWS凭证,但不知道如何操作。

12个回答

51
Pandas在read_csv内部使用boto(而不是boto3)。您可能能够安装boto并使其正常工作。
在Python 3.4.4 / Python 3.5.1上存在与boto的一些问题。如果您使用这些平台,在这些问题得到解决之前,您可以使用boto 3。
import boto3
import pandas as pd

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

obj有一个.read方法(返回字节流),这对于pandas来说已经足够了。


运行得很好。有两个问题:1. 我已经安装了boto和pandas,也成功导入了,但是我仍然收到403错误。2. 你代码的第五行应该写成obj = s3.get_object...(而不是client.get_object...)。 - IgorK
8
我正在尝试使用最新版本的pandas 0.20和boto3来尝试这种方法,看起来pandas不喜欢StreamingBody()。有没有办法转换为stringIO? - spitfiredd
2
请您能否举个例子来说明您的“bucket”和“key”。我遇到了正则表达式错误。 - Amir
'bucket' 是存储桶的名称,'key' 是存储桶中文件的路径。使用 IgorK 的示例,代码应为 s3.get_object(Bucket='mybucket', Key='file.csv')。 - Zolzaya Luvsandorj

36

针对Pandas 0.20.1进行了更新

Pandas现在使用s3fs来处理S3连接。 链接

pandas现在使用s3fs来处理S3连接。这不应该导致任何代码出现问题。但是,由于s3fs不是一个必需的依赖项,您需要单独安装它,就像之前版本的pandas需要安装boto一样。

import os

import pandas as pd
from s3fs.core import S3FileSystem

# aws keys stored in ini file in same path
# refer to boto3 docs for config settings
os.environ['AWS_CONFIG_FILE'] = 'aws_config.ini'

s3 = S3FileSystem(anon=False)
key = 'path\to\your-csv.csv'
bucket = 'your-bucket-name'

df = pd.read_csv(s3.open('{}/{}'.format(bucket, key), mode='rb'))
# or with f-strings
df = pd.read_csv(s3.open(f'{bucket}/{key}', mode='rb'))

2
这是一种非常方便的处理权限的方式。 - robertwest
使用此方法时,我遇到了一些文件未找到的错误,尽管文件存在于存储桶中,但可能是缓存(默认填充缓存实例化s3fs)正在执行其操作,或者是因为存储桶在不同区域之间不同步,导致S3试图维护读取一致性。我还无法确定有时会导致文件未找到错误的原因。 - yash

28

针对pandas 0.22及更高版本的更新:

如果您已经安装了s3fs (pip install s3fs),则可以直接从s3路径读取文件,无需任何导入:

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

稳定文档


这是否真正解决了凭据问题?Forbidden错误似乎需要角色访问权限。 - eljusticiero67

16

基于这个答案,我发现smart_open使用起来简单得多:

import pandas as pd
from smart_open import smart_open

initial_df = pd.read_csv(smart_open('s3://bucket/file.csv'))

1
我不知道其他答案是否“正确”,但我认为当你说“smart_open [is] much simpler to use.”时,你是最正确的。实际上,我刚刚发现了smart_open用于这个项目,并已经在其上开发了一些东西……但是当我在AWS Lambda中加载pandas dataframe时遇到问题。在看到你的答案之前,我不知所措。我喜欢用大约12个字符解决一个困难的问题。现在我完全爱上了smart_open <3 谢谢 :) - Amandalishus
此代码不适用于pd.read_hdf()NotImplementedError:尚未实现对通用缓冲区的支持。 - crypdick

10
import pandas as pd
import boto3
from io import StringIO

# Read CSV
s3 = boto3.client('s3',endpoint_url,aws_access_key_id=,aws_secret_access_key)
read_file = s3.get_object(Bucket, Key)
df = pd.read_csv(read_file['Body'],sep=',')

# Write CSV
csv_buffer = StringIO()
df.to_csv(csv_buffer)
s3.put_object(Bucket, Key,Body=csv_buffer.getvalue())

4
如果您回答一个旧问题,您的回答会对其他 StackOverflow 用户更有用,特别是当该问题已经有一个被采纳的答案时,如果您在回答中包含一些背景信息来解释您的答案如何帮助,那将会更好。请参阅:如何撰写一个好答案 - David Buck

3

除了其他很棒的答案外,如果需要自定义端点,可以通过猴子补丁s3fs init方法来使用pd.read_csv('s3://...')语法。

import s3fs
s3fsinit = s3fs.S3FileSystem.__init__
def s3fsinit_patched(self, *k, *kw):
    s3fsinit(self, *k, client_kwargs={'endpoint_url': 'https://yourcustomendpoint'}, **kw)
s3fs.S3FileSystem.__init__ = s3fsinit_patched

或者,更加优雅的方式:

import s3fs
class S3FileSystemPatched(s3fs.S3FileSystem):
    def __init__(self, *k, **kw):
        super(S3FileSystemPatched, self).__init__( *k,
                                                  key = os.environ['aws_access_key_id'],
                                                  secret = os.environ['aws_secret_access_key'],
                                                  client_kwargs={'endpoint_url': 'https://yourcustomendpoint'},
                                                  **kw)
        print('S3FileSystem is patched')
s3fs.S3FileSystem = S3FileSystemPatched

另请参阅:s3fs 自定义终端节点 URL


2

不使用s3fs升级pandas 0.20.3:

import boto3
import pandas as pd
import sys

if sys.version_info[0] < 3: 
    from StringIO import StringIO # Python 2.x
else:
    from io import StringIO # Python 3.x

s3 = boto3.client('s3')
obj = s3.get_object(Bucket='bucket', Key='key')
body = obj['Body']
csv_string = body.read().decode('utf-8')

df = pd.read_csv(StringIO(csv_string))

1
这个方法可以工作,但似乎会切掉列名? - Matt M

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

您也可以手动使用凭据。

0
补充其他解决方案的最新更新: pandasfsspecs3fs都已更新,因此您可以直接使用pandas从自定义端点读取,无需导入其他内容。 您必须确保安装了fsspecs3fs,因为它们是pandas的可选依赖项。

然后您可以使用

import pandas as pd

pd.read_csv(
    's3://mybucket/file.csv',
    storage_options = {
        client_kwargs = {
            'endpoint_url': <MY_S3_ENDPOINT_URL>
        }
    }

虽然有些笨重,但由于boto3的维护者们出于某种原因拒绝更新库以允许在客户端构建之外进行自定义端点配置(即在配置文件或环境变量中),这是必需的。但如果您不需要通过pandas读写数据,那么像其他人提到的那样,awswrangler可能更好。


0
请注意,如果您的存储桶是私有的并且在类似于 AWS 的提供商上,您将会遇到错误,因为 s3fs 不像 awscli 一样在 ~/.aws/config 加载配置文件。
一个解决方案是定义当前的环境变量:
export AWS_S3_ENDPOINT="myEndpoint"
export AWS_DEFAULT_REGION="MyRegion"

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