Press "Enter" to skip to content

Boto3与AWS Wrangler:使用Python简化S3操作

AWS S3开发的比较分析

Hemerson Coelho在Unsplash上的图片

介绍

在本教程中,我们将通过探索和比较两个强大的库: boto3awswrangler ,深入探讨AWS S3开发的世界。

如果您曾经想过

“与AWS S3存储桶交互的最佳Python工具是什么?”

“如何以最有效的方式执行S3操作?”

那么您来对地方了。

的确,在本文中,我们将涵盖一系列与AWS S3存储桶一起工作所必需的常见操作,其中包括:

  1. 列出对象
  2. 检查对象是否存在
  3. 下载对象
  4. 上传对象
  5. 删除对象
  6. 写入对象
  7. 读取对象(标准方式或使用SQL)

通过比较这两个库,我们将确定它们的相似之处、不同之处以及每个操作的最佳用例。最后,您将清楚地了解哪个库更适合特定的S3任务。

此外,对于那些一直阅读到最后的人,我们还将探讨如何利用boto3awswrangler使用友好的SQL查询从S3中读取数据。

因此,让我们深入探索与AWS S3交互的最佳工具,并学习如何使用Python使用这两个库高效执行这些操作。

先决条件和数据

本教程中使用的包版本为:

  • boto3==1.26.80
  • awswrangler==2.19.0

此外,已经上传了包括随机生成的account_balances数据的三个初始文件到名为coding-tutorials的S3存储桶中:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第2张

尽管您应该知道有许多方法可以建立与S3存储桶的连接,但在这种情况下,我们将使用boto3中的setup_default_session()

# 连接S3存储桶import osimport ioimport boto3import awswrangler as wrimport pandas as pdboto3.setup_default_session(aws_access_key_id = 'your_access_key',                            aws_secret_access_key = 'your_secret_access_key')bucket = 'coding-tutorials'

这种方法很方便,因为一旦会话已经设置,它可以被boto3awswrangler共享,这意味着我们不需要在后面传递任何更多的密钥。

比较分析

现在让我们比较boto3awswrangler,同时执行一些常见操作,并找出哪个工具最适合这项工作。

包括以下代码的完整笔记本可以在此GitHub文件夹中找到。

#1 列出对象

列出对象可能是我们在探索新的S3存储桶时应该执行的第一个操作,也是检查会话是否正确设置的简单方式。

使用boto3可以列出对象:

  • boto3.client('s3').list_objects()
  • boto3.resource('s3').Bucket().objects.all()
print('--BOTO3--') # BOTO3 - 首选方法client = boto3.client('s3')for obj in client.list_objects(Bucket=bucket)['Contents']:    print('文件名:', obj['Key'], '大小:', round(obj['Size']/ (1024*1024), 2), 'MB')    print('----') # BOTO3 - 替代方法resource = boto3.resource('s3')for obj in resource.Bucket(bucket).objects.all():    print('文件名:', obj.key, '大小:', round(obj.size/ (1024*1024), 2), 'MB')

尽管clientresource类都能很好地完成工作,但应优先选择client类,因为它更优雅并提供大量[易于访问的]低级元数据作为嵌套的JSON(其中包括对象size)。

另一方面,awswrangler只提供了一个列出对象的方法:

  • wr.s3.list_objects()

作为高级方法,它不返回有关对象的任何低级元数据,因此要查找文件size,我们需要调用:

  • wr.s3.size_objects
print('--AWS_WRANGLER--') # AWS WRANGLERfor obj in wr.s3.list_objects("s3://coding-tutorials/"):    print('文件名:', obj.replace('s3://coding-tutorials/', ''))    print('----') for obj, size in wr.s3.size_objects("s3://coding-tutorials/").items():    print('文件名:', obj.replace('s3://coding-tutorials/', '') , '大小:', round(size/ (1024*1024), 2), 'MB')

上面的代码返回:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第3张

比较 → Boto3 胜出

尽管awswrangler更容易使用,但在列出 S3 对象时,boto3赢得了挑战。实际上,它的低级实现意味着可以使用其类之一检索更多的对象元数据。这些信息在以编程方式访问 S3 存储桶时非常有用。

#2 检查对象是否存在

检查对象的存在性是在我们希望对已经在 S3 中可用的对象触发其他操作时所需的。

使用boto3可以执行此类检查:

  • boto3.client('s3').head_object()
object_key = 'account_balances_jan2023.parquet'# BOTO3print('--BOTO3--') client = boto3.client('s3')try:    client.head_object(Bucket=bucket, Key = object_key)    print(f"对象存在于桶{bucket}中。")except client.exceptions.NoSuchKey:    print(f"对象在桶{bucket}中不存在。")

相反,awswrangler提供了专用方法:

  • wr.s3.does_object_exist()
# AWS WRANGLERprint('--AWS_WRANGLER--') try:    wr.s3.does_object_exist(f's3://{bucket}/{object_key}')    print(f"对象存在于桶{bucket}中。")except:    print(f"对象在桶{bucket}中不存在。")

上面的代码返回:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第4张

比较 → AWSWrangler 胜出

让我们承认吧:boto3的方法名称[head_object()]并不是那么直观。

另外,awswrangler 有一个专门的方法,无疑是这场比赛的优势。

# 3 下载对象

使用以下方法,无论是 boto3 还是 awswrangler,在本地下载对象都非常简单:

  • boto3.client('s3').download_file()
  • wr.s3.download()

唯一的区别在于,download_file() 接受 bucketobject_keylocal_file 作为输入变量,而 download() 只需要 S3 的 pathlocal_file

object_key = 'account_balances_jan2023.parquet'# BOTO3client = boto3.client('s3')client.download_file(bucket, object_key, 'tmp/account_balances_jan2023_v2.parquet')# AWS WRANGLERwr.s3.download(path=f's3://{bucket}/{object_key}', local_file='tmp/account_balances_jan2023_v3.parquet')

当代码执行时,相同对象的两个版本确实都会被下载到本地的 tmp/ 文件夹内:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第5张

比较 → 平局

就下载文件而言,我们可以认为这两个库是等价的,因此让我们将其视为平局。

# 4 上传对象

在从本地环境上传文件到 S3 时,同样的原理适用。可以使用以下方法:

  • boto3.client('s3').upload_file()
  • wr.s3.upload()
object_key_1 = 'account_balances_apr2023.parquet'object_key_2 = 'account_balances_may2023.parquet'file_path_1 = os.path.dirname(os.path.realpath(object_key_1)) + '/' + object_key_1file_path_2 = os.path.dirname(os.path.realpath(object_key_2)) + '/' + object_key_2# BOTO3client = boto3.client('s3')client.upload_file(file_path_1, bucket, object_key_1)# AWS WRANGLERwr.s3.upload(local_file=file_path_2, path=f's3://{bucket}/{object_key_2}')

执行代码后,将两个新的 account_balances 对象(2023 年 4 月和 5 月份的)上传到 coding-tutorials 存储桶中:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第6张

比较 → 平局

这又是一场平局。到目前为止,这两个库之间存在绝对的平衡!

# 5 删除对象

现在假设我们希望删除以下对象:

#SINGLE OBJECTobject_key = ‘account_balances_jan2023.parquet’#MULTIPLE OBJECTSobject_keys = [‘account_balances_jan2023.parquet’,                ‘account_balances_feb2023.parquet’,                ‘account_balances_mar2023.parquet’]

boto3 允许逐个或批量删除对象,使用以下方法:

  • boto3.client('s3').delete_object()
  • boto3.client('s3').delete_objects()

这两种方法都返回一个 response,其中包括 ResponseMetadata,可用于验证对象是否已成功删除。例如:

  • 删除单个对象时,HTTPStatusCode==204 表示操作已成功完成(如果在 S3 存储桶中找到对象);
  • 删除多个对象时,返回一个 Deleted 列表,其中包含已成功删除的项的名称。
# BOTO3print('--BOTO3--')client = boto3.client('s3')# 删除单个对象response = client.delete_object(Bucket=bucket, Key=object_key)deletion_date = response['ResponseMetadata']['HTTPHeaders']['date']if response['ResponseMetadata']['HTTPStatusCode'] == 204:    print(f'对象 {object_key} 在 {deletion_date} 成功删除。')else:    print(f'对象无法删除。')# 删除多个对象objects = [{'Key': key} for key in object_keys]response = client.delete_objects(Bucket=bucket, Delete={'Objects': objects})deletion_date = response['ResponseMetadata']['HTTPHeaders']['date']if len(object_keys) == len(response['Deleted']):    print(f'所有对象在 {deletion_date} 成功删除。')else:    print(f'对象无法删除。')

另一方面,awswrangler 提供了一种可用于单个和批量删除的方法:

  • wr.s3.delete_objects()

由于可以将object_keys递归传递给该方法作为list_comprehension,而不像之前那样先转换为字典,因此使用这种语法非常方便。

# AWS WRANGLERprint('--AWS_WRANGLER--')# 删除单个对象wr.s3.delete_objects(path=f's3://{bucket}/{object_key}')# 删除多个对象try:    wr.s3.delete_objects(path=[f's3://{bucket}/{key}' for key in object_keys])    print('所有对象已成功删除。')except:    print(f'对象无法删除。')

执行上述代码,将删除S3中的对象,然后返回:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第7张

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第8张

比较 → Boto3 胜出

这个有些棘手:删除多个对象时,awswrangler具有更简单的语法,因为我们可以直接将完整列表传递给该方法。

然而,boto3在以编程方式删除对象时返回大量信息的response,这些信息是非常有用的日志。

因为在生产环境中,低级元数据优于几乎没有元数据,所以boto3赢得了这场比赛,现在成绩是2-1。

# 6 写入对象

当涉及到将文件写入S3时,boto3甚至没有提供开箱即用的方法来执行此类操作。

例如,如果我们想使用boto3创建一个新的parquet文件,我们首先需要将对象持久化到本地磁盘上(使用pandasto_parquet()方法),然后使用upload_fileobj()方法将其上传到S3。

upload_file()(在第4点中探讨)不同,upload_fileobj()方法是一个托管传输,如果必要将执行多线程的分块上传:

object_key_1 = 'account_balances_june2023.parquet'# 运行 generator.py 脚本df.to_parquet(object_key_1)# BOTO3client = boto3.client('s3')# 将 Parquet 文件上传到 S3with open(object_key_1, 'rb') as file:    client.upload_fileobj(file, bucket, object_key_1)

另一方面,awswrangler库(在使用pandas时)的主要优势之一是,它可以直接将对象写入S3桶(而无需将它们保存到本地磁盘),这既优雅又高效。

此外,awswrangler提供了很大的灵活性,允许用户:

  • 应用特定的压缩算法,如snappygzipzstd
  • 通过dataset = True时的mode参数appendoverwrite现有文件;
  • 通过partitions_col参数指定一个或多个分区列。
object_key_2 ='account_balances_july2023.parquet'# AWS WRANGLER   wr.s3.to_parquet(df=df,                  path=f's3://{bucket}/{object_key_2}',                  compression ='gzip',                  partition_cols =['COMPANY_CODE'],                  dataset=True)

执行后,上述代码将account_balances_june2023作为单个parquet文件写入,并将account_balances_july2023作为已通过COMPANY_CODE进行分区的四个文件夹写入:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第9张

比较 → AWSWrangler胜出

如果使用pandas是一种选择,awswrangler在将文件写入S3时提供了一组更高级的操作,尤其是与在这种情况下并不是最好的工具的boto3相比。

# 7.1 R 读取对象(Python)

当使用boto3从S3读取对象时,同样的推理适用:由于该库没有内置的读取方法,我们最好的选择是执行API调用(get_object()),读取responseBody,然后将parquet_object传递给pandas

请注意,pd.read_parquet()方法期望输入为类似文件的对象,这就是为什么我们需要将从parquet_object中读取的内容作为二进制流传递的原因。

实际上,通过使用io.BytesIO(),我们在内存中创建了一个临时文件对象,避免了需要在读取之前将Parquet文件保存在本地的需要。这反过来可以提高性能,特别是在处理大文件时:

object_key = 'account_balances_may2023.parquet'#BOTO3client = boto3.client('s3')#读取Parquet文件response = client.get_object(Bucket=bucket,Key=object_key)parquet_object = response['Body'].read()df = pd.read_parquet(io.BytesIO(parquet_object))df.head()

如预期的那样,awswrangler在从S3读取对象方面表现出色,以pandas df作为输出返回。

它支持多种输入格式,如csvjsonparquet和最近的delta表。同时,传递chunked参数可以以内存友好的方式读取对象:

# AWS WRANGLERdf = wr.s3.read_parquet(path=f's3://{bucket}/{object_key}')df.head()# wr.s3.read_csv()# wr.s3.read_json()# wr.s3.read_parquet_table()# wr.s3.read_deltalake()

执行上述代码将返回一个带有5月数据的pandas df:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第10张

比较 → AWSWrangler胜出

是的,有绕过boto3缺少适当方法的方法。但是,awswrangler是一个旨在高效读取S3对象的库,因此它也赢得了这个挑战。

# 7.2 读取对象(SQL)

读到这里的人应该得到一个奖励,这个奖励是使用纯 SQL 从 S3 中读取对象。

假设我们希望使用以下 query(通过 AS_OF_DATE 过滤数据)从 account_balances_may2023.parquet 对象中获取数据:

object_key = 'account_balances_may2023.parquet'query = """SELECT * FROM s3object s            WHERE AS_OF_DATE > CAST('2023-05-13T' AS TIMESTAMP)"""

boto3 中,可以通过 select_object_content() 方法实现。请注意,我们还应该指定 inputSerializationOutputSerialization 格式:

# BOTO3client = boto3.client('s3')resp = client.select_object_content(        Bucket=bucket,        Key=object_key,        Expression= query,        ExpressionType='SQL',        InputSerialization={"Parquet": {}},        OutputSerialization={'JSON': {}},)records = []# 处理响应数据for event in resp['Payload']:    if 'Records' in event:        records.append(event['Records']['Payload'].decode('utf-8'))        # 将 JSON 记录连接成单个字符串json_string = ''.join(records)# 将 JSON 数据加载到 Pandas DataFramedf = pd.read_json(json_string, lines=True)# 打印 DataFramedf.head()

如果可以使用 pandas df,awswrangler 还提供了非常方便的 select_query() 方法,需要很少的代码:

# AWS WRANGLERdf = wr.s3.select_query(        sql=query,        path=f's3://{bucket}/{object_key}',        input_serialization="Parquet",        input_serialization_params={})df.head()

对于这两个库,返回的 df 将如下所示:

Boto3与AWS Wrangler:使用Python简化S3操作 数据科学 第10张

结论

在本教程中,我们探讨了可以在 S3 存储桶上执行的 7 个常见操作,并对 boto3awswrangler 库进行了比较分析。

这两种方法都允许我们与 S3 存储桶进行交互,但主要区别在于 boto3 客户端提供了对 AWS 服务的低级访问,而 awswrangler 为各种数据工程任务提供了简化和更高级别的接口。

总的来说,awswrangler 是我们的赢家,拿到了 3 分(检查对象存在性、写入对象、读取对象),而 boto3 仅得到了 2 分(列出对象、删除对象)。上传/下载对象类别均为平局,未分配分数。

尽管如上结果,事实上,当相互使用时,两个库都会在它们为之建立的任务中发挥出最佳水平。

来源

  • AWS Wrangler 文档
  • Boto3 S3 客户端文档

除非另有说明,所有图片均由作者提供。

Leave a Reply

Your email address will not be published. Required fields are marked *