使用Python的boto3从S3读取JSON文件

102

我一直在跟随S3存储桶“test”中的JSON文件

{
  'Details' : "Something" 
}
我正在使用以下代码读取此JSON并打印键'Details'。

我正在使用以下代码读取此JSON并打印键'Details'

s3 = boto3.resource('s3',
                    aws_access_key_id=<access_key>,
                    aws_secret_access_key=<secret_key>
                    )
content_object = s3.Object('test', 'sample_json.txt')
file_content = content_object.get()['Body'].read().decode('utf-8')
json_content = json.loads(repr(file_content))
print(json_content['Details'])

我遇到了错误"string indices must be integers",不想从S3下载文件后再读取文件。


6
移除 repr - Alex Hall
1
@AlexHall 最初我尝试删除 repr,但它没有起作用,会出现 ValueError: Expecting property name enclosed in double quotes - Nanju
4
我解决了问题。JSON 应该用双引号括起属性。我改变了我的 JSON 格式。 - Nanju
1
你在哪一行遇到了错误?将该行拆分开。file_content = content_object... 这一行包含了4个步骤。现在,将其拆分为4个单独的行,并使用4个中间变量进行赋值。然后查看哪一行出错了。 - falsePockets
我遇到的问题只需要使用'.read().decode('utf-8')',所以感谢你的询问(-; - soBusted
7个回答

108

如上面的评论所提到的,必须删除 repr ,并且在 json 文件中使用 双引号 作为属性的引用符。在 aws/s3 上使用此文件:

{
  "Details" : "Something"
}

并且以下Python代码可以正常运行:

import boto3
import json

s3 = boto3.resource('s3')

content_object = s3.Object('test', 'sample_json.txt')
file_content = content_object.get()['Body'].read().decode('utf-8')
json_content = json.loads(file_content)
print(json_content['Details'])
# >> Something

41
其他人请注意:https://boto3.readthedocs.io/en/latest/reference/services/s3.html#object 中的s3.Object('bucketName', 'keyName'),获取文件s3://foobarBucketName/folderA/folderB/myFile.json的示例代码为s3.Object('foobarBucketName', 'folderA/folderB/myFile.json') - Kyle Bridenstine

49
以下方法对我有效。
# read_s3.py

from boto3 import client

BUCKET = 'MY_S3_BUCKET_NAME'
FILE_TO_READ = 'FOLDER_NAME/my_file.json'
client = client('s3',
                 aws_access_key_id='MY_AWS_KEY_ID',
                 aws_secret_access_key='MY_AWS_SECRET_ACCESS_KEY'
                )
result = client.get_object(Bucket=BUCKET, Key=FILE_TO_READ) 
text = result["Body"].read().decode()
print(text['Details']) # Use your desired JSON Key for your value 

进一步改进
让我们将上面的代码片段称为read_s3.py
直接硬编码AWS Id和Secret Keys并不是一个好主意。为了最佳实践,您可以考虑以下任一方法:
(1)从存储在本地存储中的json文件(aws_cred.json)中读取您的AWS凭据:
from json import load
from boto3 import client
...
credentials = load(open('local_fold/aws_cred.json'))
client = client('s3',
                 aws_access_key_id=credentials['MY_AWS_KEY_ID'],
                 aws_secret_access_key=credentials['MY_AWS_SECRET_ACCESS_KEY']
                )

(2)从您的环境变量中读取(我在部署时的首选选项):
    from os import environ
    client = boto3.client('s3',              
                         aws_access_key_id=environ['MY_AWS_KEY_ID'],
                           aws_secret_access_key=environ['MY_AWS_SECRET_ACCESS_KEY']
                         )

让我们准备一个名为read_s3_using_env.sh的shell脚本来设置环境变量,并将我们的Python脚本(read_s3.py)添加到其中,如下所示:
# read_s3_using_env.sh
export MY_AWS_KEY_ID='YOUR_AWS_ACCESS_KEY_ID'
export MY_AWS_SECRET_ACCESS_KEY='YOUR_AWS_SECRET_ACCESS_KEY'
# execute the python file containing your code as stated above that reads from s3
python read_s3.py # will execute the python script to read from s3

现在在终端中执行以下命令来运行shell脚本:
sh read_s3_using_env.sh

1
您无需在客户端初始化中指定凭据,boto3和其他AWS SDK会自动处理它们。这使得用户可以通过他们选择的任何方式(例如IAM角色)自动进行身份验证。 - Pedro
2
@Hafizur Rahman- 这里的变量 text 是一个字符串,所以 print(text['Details']) 不会起作用。我相信你需要相应地更新代码片段。 - Varun

39

我想补充一下,botocore.response.streamingbodyjson.load 配合使用效果很好:

import json
import boto3

s3 = boto3.resource('s3')

obj = s3.Object(bucket, key)
data = json.load(obj.get()['Body']) 

请注意:此处无法使用json.loads(带s)。 - Pedro

5
您可以在 AWS Lambda 中使用以下代码,从 S3 存储桶中读取 JSON 文件并使用 Python 进行处理。
import json
import boto3
import sys
import logging

# logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

VERSION = 1.0

s3 = boto3.client('s3')

def lambda_handler(event, context):
    bucket = 'my_project_bucket'
    key = 'sample_payload.json'
    
    response = s3.get_object(Bucket = bucket, Key = key)
    content = response['Body']
    jsonObject = json.loads(content.read())
    print(jsonObject)

4
我遇到了一些问题,因为解码对我不起作用(s3对象是经过gzip压缩的)。
发现这个讨论对我有帮助: Python gzip: is there a way to decompress from a string?
import boto3
import zlib

key = event["Records"][0]["s3"]["object"]["key"]
bucket_name = event["Records"][0]["s3"]["bucket"]["name"]

s3_object = S3_RESOURCE.Object(bucket_name, key).get()['Body'].read()

jsonData = zlib.decompress(s3_object, 16+zlib.MAX_WBITS)

如果你打印jsonData,你会看到你想要的JSON文件!如果你是在AWS中运行测试,请务必检查CloudWatch日志,因为在lambda中,如果JSON文件太长,它不会输出完整的JSON文件。


1
这很容易使用cloudpathlib完成,它支持S3、Google Cloud Storage和Azure Blob Storage。以下是一个示例:
import json
from cloudpathlib import CloudPath


# first, we'll write some json data so then we can later read it
CloudPath("s3://mybucket/asdf.json").write_text('{"field": "value"}')
#> 18


# read data from S3
data = json.loads(
    CloudPath("s3://mybucket/asdf.json").read_text()
)

# look at the data
data
#> {'field': 'value'}

# access it now that it is loaded in Python
data["field"] == "value"
#> True

这带来了一些额外的好处,比如设置特定选项不同的身份验证机制,或者保持持久缓存,这样您就不必总是从S3重新下载。

0
如果您的JSON文件看起来像这样:
{
    "test": "test123"
}

你可以像访问字典一样访问它:

BUCKET="Bucket123"

def get_json_from_s3(key: str):
    """
    Retrieves the json file containing responses from s3. returns a dict

    Args:
        key (str): file path to the json file

    Returns:
        dict: json style dict
    """
    data = client.get_object(Bucket=BUCKET, Key=key)
    json_text = data["Body"].read().decode("utf-8")
    json_text_object = json.loads(json_text)
    return json_text_object
test_dict = get_json_from_s3(key="test.json")
print(test_dict["test"])

这里的变量名有误导性。json_text_bytes 包含 JSON 文本,而 json_text 包含 JSON 对象。 - Tomer

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