使用boto从S3中逐行读取文件?

时间:2015-02-19 22:42:49

标签: python amazon-s3 boto

我在S3中有一个csv文件,我正在尝试读取标题行以获取大小(这些文件由我们的用户创建,因此它们几乎可以是任何大小)。有没有办法用boto做到这一点?我想也许我可以使用python BufferedReader,但我无法弄清楚如何从S3键打开流。任何建议都会很棒。谢谢!

10 个答案:

答案 0 :(得分:23)

您可能会发现https://pypi.python.org/pypi/smart_open对您的任务非常有用。

来自文档:

for line in smart_open.smart_open('s3://mybucket/mykey.txt'):
    print line

答案 1 :(得分:11)

看来boto有一个read()函数可以做到这一点。这里有一些适合我的代码:

>>> import boto
>>> from boto.s3.key import Key
>>> conn = boto.connect_s3('ap-southeast-2')
>>> bucket = conn.get_bucket('bucket-name')
>>> k = Key(bucket)
>>> k.key = 'filename.txt'
>>> k.open()
>>> k.read(10)
'This text '

read(n)的调用将返回对象的下一个n个字节。

当然,这不会自动返回“标题行”,但您可以使用足够大的数字来调用它以至少返回标题行。

答案 2 :(得分:7)

这是一个逐行实际流式传输数据的解决方案:

from io import TextIOWrapper
from gzip import GzipFile
...

# get StreamingBody from botocore.response
response = s3.get_object(Bucket=bucket, Key=key)
# if gzipped
gzipped = GzipFile(None, 'rb', fileobj=response['Body'])
data = TextIOWrapper(gzipped)

for line in data:
    # process line

答案 3 :(得分:5)

使用boto3,您可以访问原始流并逐行读取。 请注意原始流由于某种原因是私有财产

s3 = boto3.resource('s3', aws_access_key_id='xxx', aws_secret_access_key='xxx')
obj = s3.Object('bucket name', 'file key')

obj.get()['Body']._raw_stream.readline() # line 1
obj.get()['Body']._raw_stream.readline() # line 2
obj.get()['Body']._raw_stream.readline() # line 3...

答案 4 :(得分:3)

stdlib中的codecs module提供了一种简单的方法来将字节流编码为文本流,并提供了生成器来逐行检索此文本。可以轻松地与S3一起使用:

import codecs

import boto3


s3 = boto3.resource("s3")
s3_object = s3.Object('my-bucket', 'a/b/c.txt')
line_stream = codecs.getreader("utf-8")

for line in line_stream(s3_object.get()['Body']):
    print(line)

答案 5 :(得分:2)

使用boto3:

s3 = boto3.resource('s3')
obj = s3.Object(BUCKET, key)
for line in obj.get()['Body']._raw_stream:
    # do something with line

答案 6 :(得分:2)

我知道这是一个非常老的问题。

但是到目前为止,我们只能使用__root__

答案 7 :(得分:1)

扩展kooshywoosh的答案:直接在纯二进制文件中无法在StreamingBody上使用TextIOWrapper(这非常有用),因为您会得到以下错误:

var list = new List<object> {"lol", 101};

foreach (var value in list)
{
    if(value is string s)
        Console.WriteLine(s);
    if (value is int i)
        Console.WriteLine(i);
}

但是,您可以使用botocore的github页面上this长期存在的问题中提到的以下技巧,并围绕StreamingBody定义一个非常简单的包装器类:

"builtins.AttributeError: 'StreamingBody' object has no attribute 'readable'"

然后,您可以简单地使用以下代码:

from io import RawIOBase
...

class StreamingBodyIO(RawIOBase):
"""Wrap a boto StreamingBody in the IOBase API."""
def __init__(self, body):
    self.body = body

def readable(self):
    return True

def read(self, n=-1):
    n = None if n < 0 else n
    return self.body.read(n)

答案 8 :(得分:0)

如果您要读取多个文件(具有逐行的地址)并具有特定的存储区前缀(例如,在“子文件夹”中),则可以执行以下操作:

s3 = boto3.resource('s3', aws_access_key_id='<key_id>', aws_secret_access_key='<access_key>')

    bucket = s3.Bucket('<bucket_name>')
    for obj in bucket.objects.filter(Prefix='<your prefix>'):
        for line in obj.get()['Body'].read().splitlines():
            print(line.decode('utf-8'))

这里的行是字节,所以我正在解码它们;但是如果它们已经是字符串,则可以跳过。

答案 9 :(得分:0)

读取文件的最动态,最低成本的方法是读取每个字节,直到找到所需的行数为止。

line_count = 0
line_data_bytes = b''

while line_count < 2 :

    incoming = correlate_file_obj['Body'].read(1)
    if incoming == b'\n':
        line_count = line_count + 1

    line_data_bytes = line_data_bytes + incoming

logger.debug("read bytes:")
logger.debug(line_data_bytes)

line_data = line_data_bytes.split(b'\n')

如果标头大小可以更改,则无需猜测标头大小,您也不必最终下载整个文件,并且不需要第三方工具。当然,您需要确保文件中的行距正确无误,并且正在读取正确的字节数才能找到它。