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

Posted

技术标签:

【中文标题】将文件从私有 S3 存储桶读取到 pandas 数据帧【英文标题】:Reading a file from a private S3 bucket to a pandas dataframe 【发布时间】:2016-06-18 15:12:53 【问题描述】:

我正在尝试将 CSV 文件从私有 S3 存储桶读取到 pandas 数据帧:

df = pandas.read_csv('s3://mybucket/file.csv')

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

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

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

【问题讨论】:

【参考方案1】:

要在此处添加更多最新更新的其他解决方案: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 可能会像其他人提到的那样更好。

【讨论】:

【参考方案2】:

前面的答案是一个很好的基本开始,但我想实现下面所述的高级目标。总的来说,我觉得awswrangler 是要走的路。

    读取.gzip 只读取前 5 行而不下载完整文件 明确传递凭据(确保您没有将它们提交给代码!!) 使用完整的 s3 路径

以下是一些有效的方法


import boto3
import pandas as pd
import awswrangler as wr

boto3_creds = dict(region_name="us-east-1", aws_access_key_id='', aws_secret_access_key='')
boto3.setup_default_session(**boto3_creds)

s3 = boto3.client('s3')

# read first 5 lines from file path
obj = s3.get_object(Bucket='bucket', Key='path.csv.gz')
df = pd.read_csv(obj['Body'], nrows=5, compression='gzip')

# read first 5 lines from directory
dft_xp = pd.concat(list(wr.s3.read_csv(wr.s3.list_objects('s3://bucket/path/')[0], chunksize=5, nrows=5, compression='gzip')))

# read all files into pandas
df_xp = wr.s3.read_csv(wr.s3.list_objects('s3://bucket/path/'), compression='gzip')

没有使用 s3fs 不确定是否使用 boto3。

对于使用 dask 的分布式计算,这可行,但它使用 s3fs afaik 并且显然 gzip 无法并行化。


import dask.dataframe as dd

dd.read_csv('s3://bucket/path/*', storage_options='key':'', 'secret':'', compression='gzip').head(5)

dd.read_csv('s3://bucket/path/*', storage_options='key':'', 'secret':'', compression='gzip') 
# Warning gzip compression does not support breaking apart files Please ensure that each individual file can fit in memory
 

【讨论】:

【参考方案3】:

为 Pandas 0.20.1 更新

Pandas 现在使用 s3fs 来处理 s3 连接。 link

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

【讨论】:

这是一种非常方便的权限处理方式【参考方案4】:
import s3fs
import pandas as pd
s3 = s3fs.S3FileSystem(profile='<profile_name>')
pd.read_csv(s3.open(<s3_path>))

您也可以手动使用凭据。

【讨论】:

【参考方案5】:

除了其他很棒的答案之外,如果需要自定义端点,可以通过猴子修补 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 custom endpoint url

【讨论】:

【参考方案6】:
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())

【讨论】:

在回答一个老问题时,如果您包含一些上下文来解释您的答案如何提供帮助,那么您的答案将对其他 *** 用户更有用,特别是对于已经有一个已接受答案的问题。请参阅:How do I write a good answer。【参考方案7】:

请注意,如果您的存储桶是私有的并且在 aws 类 提供程序上,您将遇到错误,因为 s3fs 不会像 awscli 一样在 ~/.aws/config 加载配置文件配置文件。

一种解决方案是定义当前环境变量:

export AWS_S3_ENDPOINT="myEndpoint"
export AWS_DEFAULT_REGION="MyRegion"

【讨论】:

【参考方案8】:

pandas 0.22 及更高版本的更新:

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

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

stable docs

【讨论】:

【参考方案9】:

基于this answer,我发现smart_open使用起来要简单得多:

import pandas as pd
from smart_open import smart_open

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

【讨论】:

我不知道任何或所有其他答案是否“正确”,但我相信当您说“smart_open [is] 更易于使用”时,您是最正确的。实际上,我刚刚为这个项目发现了 smart_open,并且已经在它上面开发了一些硬壳……但后来我在 AWS Lambda 中加载 pandas 数据帧时遇到了问题。在看到您的回答之前,我不知所措。我喜欢用大约 12 个字符解决一个难题。现在我完全爱上了 smart_open 这不适用于pd.read_hdf()NotImplementedError: Support for generic buffers has not been implemented.【参考方案10】:

不使用 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))

【讨论】:

这个方法有效,但是好像去掉了列名?【参考方案11】:

Pandas 在read_csv 中使用boto(不是boto3)。您也许可以安装 boto 并使其正常工作。

some troubles 带有 boto 和 python 3.4.4 / python3.5.1。如果您在这些平台上,并且在这些平台得到修复之前,您可以将 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.你的代码的第5行应该是obj = s3.get_object...(而不是client.get_object.. .) 我正在用最新版本的 pandas 0.20 和 boto3 尝试这种方法,看起来 pandas 不喜欢 StreamingBody()。有没有办法转换成stringIO? 你能举一个你的'bucket'和'key'的例子吗?我得到正则表达式错误。 'bucket' 为存储桶的名称,'key' 为存储桶中文件的路径。使用 IgorK 的示例,它将是 s3.get_object(Bucket='mybucket', Key='file.csv')

以上是关于将文件从私有 S3 存储桶读取到 pandas 数据帧的主要内容,如果未能解决你的问题,请参考以下文章

使用 lambda 中的 pandas 从 s3 读取 excel 文件并转换为 csv

如何从 S3 存储桶中读取最后修改的 csv 文件?

从 S3 存储桶中读取大量 CSV 文件

如何将 s3 数据从一个 EMR 集群读取到另一个 EMR 集群?

scala- 从 S3 存储桶读取文件

在 Amazon S3 中将在 aws3 存储桶中创建的文件夹设为公共或私有文件夹