web-dev-qa-db-fra.com

Lire un fichier ligne par ligne depuis S3 en utilisant boto?

J'ai un fichier CSV en S3 et j'essaie de lire la ligne d'en-tête pour obtenir la taille (ces fichiers sont créés par nos utilisateurs et peuvent donc être de n'importe quelle taille). Y a-t-il un moyen de faire cela en utilisant boto? Je pensais que je pourrais peut-être utiliser un BufferReader en python, mais je n'arrive pas à comprendre comment ouvrir un flux à partir d'une clé S3. Toute suggestion sera appréciée. Merci!

12
gignosko

Il semble que boto ait une fonction read() capable de le faire. Voici un code qui fonctionne pour moi:

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

L'appel à read(n) renvoie les n prochains octets de l'objet.

Bien sûr, cela ne retournera pas automatiquement "la ligne d'en-tête", mais vous pouvez l'appeler avec un nombre suffisamment grand pour renvoyer la ligne d'en-tête au minimum.

10
John Rotenstein

Vous pouvez trouver https://pypi.python.org/pypi/smart_open utile pour votre tâche.

De la documentation:

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

Voici une solution qui diffuse les données ligne par ligne:

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
6
kooshywoosh

Avec boto3, vous pouvez accéder à un flux brut et lire ligne par ligne ..__ Notez que le flux brut est une propriété privée pour une raison quelconque

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
robertzp

Utiliser boto3:

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

Si vous souhaitez lire plusieurs fichiers (ligne par ligne) avec un préfixe de compartiment spécifique (c'est-à-dire dans un "sous-dossier"), vous pouvez procéder comme suit:

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

Ici les lignes sont des octets alors je les décode; mais si elles sont déjà une chaîne, vous pouvez ignorer cela.

0
oneschilling

Le moyen le plus dynamique et le moins coûteux de lire le fichier est de lire chaque octet jusqu'à ce que vous trouviez le nombre de lignes dont vous avez besoin.

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

Vous n'aurez pas besoin de deviner la taille de l'en-tête si celle-ci peut changer, vous ne finirez pas par télécharger tout le fichier et vous n'avez pas besoin d'outils tiers. Cela dit, vous devez vous assurer que le séparateur de ligne de votre fichier est correct et que vous lisez le nombre d'octets correct pour le trouver.

0
KiteCoder