Прочитать файл в буфере из FTP python

Я пытаюсь прочитать файл с FTP-сервера. Это .gz файл. Я хотел бы знать, могу ли я выполнять действия с этим файлом, пока сокет открыт. Я пытался следовать тому, что было упомянуто в двух вопросах StackOverflow на чтение файлов без записи на диск и чтение файлов с FTP без загрузки, но безуспешно.

Я знаю, как извлекать данные / работать с загруженным файлом, но не уверен, что смогу сделать это на лету. Есть ли способ подключиться к сайту, получить данные в буфере, возможно, выполнить извлечение данных и выйти?

При попытке StringIO я получил ошибку:

>>> from ftplib import FTP
>>> from StringIO import StringIO
>>> ftp = FTP('ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/PMC-ids.csv.gz')

Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
ftp = FTP('ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/PMC-ids.csv.gz')
File "C:\Python27\lib\ftplib.py", line 117, in __init__
self.connect(host)
File "C:\Python27\lib\ftplib.py", line 132, in connect
self.sock = socket.create_connection((self.host, self.port), self.timeout)
File "C:\Python27\lib\socket.py", line 553, in create_connection
for res in getaddrinfo(host, port, 0, SOCK_STREAM):
gaierror: [Errno 11004] getaddrinfo failed

Мне просто нужно знать, как я могу получить данные в какую-то переменную и зацикливаться на ней, пока файл с FTP не будет прочитан.

Я ценю ваше время и помощь. Спасибо!


person smandape    schedule 12.09.2013    source источник
comment
Вам нужно прочитать файл в локальный буфер (например, read ()) или управлять им удаленно с помощью команд FTP?   -  person Stefano Sanfilippo    schedule 12.09.2013
comment
Я хочу управлять им удаленно, используя FTP. Поправьте меня, если я ошибаюсь, но если я прочитаю его в локальный буфер, будет ли это означать загрузку файла?   -  person smandape    schedule 12.09.2013
comment
Я имею в виду, вы хотите перенести данные с FTP-сервера на свой компьютер, а затем использовать их, верно? (вот что происходит в вопросе SO, который вы связали)   -  person Stefano Sanfilippo    schedule 12.09.2013
comment
Прошу прощения за недоразумение, но я не хочу передавать данные с сервера на свой компьютер.   -  person smandape    schedule 12.09.2013
comment
Итак, вы хотите обрабатывать данные на сервере, а затем передавать результаты на свой компьютер? Или что? Просьба уточнить.   -  person Stefano Sanfilippo    schedule 12.09.2013
comment
Именно это я и ищу. Зайдите на сервер, возьмите этот файл и обработайте его прямо здесь, и получите результаты обратно на мой компьютер.   -  person smandape    schedule 12.09.2013
comment
Так, например, вы хотите извлечь третий столбец из каждой строки CSV, а затем получить только список третьих столбцов?   -  person Stefano Sanfilippo    schedule 12.09.2013
comment
позвольте нам продолжить это обсуждение в чате   -  person smandape    schedule 12.09.2013


Ответы (3)


Обязательно сначала войдите на ftp-сервер. После этого используйте retrbinary, который извлекает файл в двоичном режиме. Он использует обратный вызов для каждого фрагмента файла. Вы можете использовать это, чтобы загрузить его в строку.

from ftplib import FTP
ftp = FTP('ftp.ncbi.nlm.nih.gov')
ftp.login() # Username: anonymous password: anonymous@

# Setup a cheap way to catch the data (could use StringIO too)
data = []
def handle_binary(more_data):
    data.append(more_data)

resp = ftp.retrbinary("RETR pub/pmc/PMC-ids.csv.gz", callback=handle_binary)
data = "".join(data)

Бонусные баллы: как насчет того, чтобы распаковать строку, пока мы на ней?

Простой режим, используя строку данных выше

import gzip
import StringIO
zippy = gzip.GzipFile(fileobj=StringIO.StringIO(data))
uncompressed_data = zippy.read()

Немного лучше, полное решение:

from ftplib import FTP
import gzip
import StringIO

ftp = FTP('ftp.ncbi.nlm.nih.gov')
ftp.login() # Username: anonymous password: anonymous@

sio = StringIO.StringIO()
def handle_binary(more_data):
    sio.write(more_data)

resp = ftp.retrbinary("RETR pub/pmc/PMC-ids.csv.gz", callback=handle_binary)
sio.seek(0) # Go back to the start
zippy = gzip.GzipFile(fileobj=sio)

uncompressed = zippy.read()

На самом деле, было бы намного лучше распаковывать на лету, но я не вижу способа сделать это с помощью встроенных библиотек (по крайней мере, не легко).

person Kyle Kelley    schedule 12.09.2013
comment
Спасибо за ответ. У меня быстрый вопрос, загружаются ли данные на мой компьютер или нет? Если не где хранятся данные? - person smandape; 13.09.2013
comment
Он хранит его в памяти в строке с именем data (или в несжатом виде, если вы идете полностью). - person Kyle Kelley; 13.09.2013
comment
Итак, последняя переменная, содержащая данные, будет несжатой, верно? - person smandape; 13.09.2013
comment
Да, с нижней частью кода данные будут несжатыми. Бац! Теперь у вас есть файл csv, который вы можете проанализировать с помощью встроенной библиотеки csv или замечательной библиотеки pandas. - person Kyle Kelley; 13.09.2013
comment
Без проблем. У меня тоже есть код в записной книжке: nbviewer .ipython.org / url / nb.fict.io / - person Kyle Kelley; 13.09.2013
comment
@smandape, обратите внимание, что вы не обрабатываете данные удаленно, как вы просили ... - person Stefano Sanfilippo; 14.09.2013
comment
Я не уверен, почему, но один из них - заменить StringIO на BytesIO, чтобы этот фрагмент работал с Python 3.4 - person tagoma; 10.02.2015
comment
Использование BytesIO отлично подходит для извлечения 4-байтовых реалов с помощью python 3.4. Хорошая работа Кайл - person captain_M; 22.04.2016
comment
На самом деле вам не нужна функция handle_binary. Просто используйте callback=data.append или callback=sio.write соответственно. - person Martin Prikryl; 15.04.2019

Я могу придумать два простых способа загрузить файл с помощью FTP и сохранить его локально:

  1. Использование ftplib:

    from ftplib import FTP
    
    ftp = FTP('ftp.ncbi.nlm.nih.gov')
    ftp.login()
    ftp.cwd('pub/pmc')
    ftp.retrbinary('RETR PMC-ids.csv.gz', open('PMC-ids.csv.gz', 'wb').write)
    ftp.quit()
    
  2. Использование urllib

    from urllib import urlretrieve
    
    urlretrieve("ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/PMC-ids.csv.gz", "PMC-ids.csv.gz")
    

Если вы не хотите загружать и сохранять его в файл, но хотите обрабатывать его постепенно по мере поступления, я предлагаю использовать urllib2:

from urllib2 import urlopen

u = urlopen("ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/readme.txt")

for line in u:
   print line

который печатает ваш файл построчно.

person nickie    schedule 12.09.2013
comment
Я могу ошибаться, но в варианте 1 не будет ли он перезаписывать файл следующим фрагментом, если чтение двоичного файла занимает более одного фрагмента? не следует указывать открытие как 'ab', а не 'wb' - person Tom Busby; 09.01.2015
comment
@TomBusby, нет, 'wb' в порядке. Передача параметров в Python активна (по значению). Обратный вызов, переданный методу retrbinary, является лишь вторым параметром. Он быстро вычисляется, поэтому open(..., 'wb') вычисляется только один раз, а метод write возвращаемого файлового объекта является обратным вызовом, который передается в retrbinary. Файл открывается только один раз для записи, а не каждый раз, когда вызывается обратный вызов, как вы могли подумать. - person nickie; 21.01.2015

Это невозможно. Чтобы обрабатывать данные на сервере, вам необходимо иметь какие-то разрешения на выполнение, будь то сценарий оболочки, который вы отправляете, или доступ к SQL.

FTP - это чистая передача файлов, выполнение не разрешено. Вам нужно будет либо включить доступ по SSH, либо загрузить данные в базу данных и получить доступ к ней с помощью запросов, либо загрузить файл с помощью urllib, а затем обработать его локально, например:

import urllib
handle = urllib.urlopen('ftp://ftp.ncbi.nlm.nih.gov/pub/pmc/PMC-ids.csv.gz')
# Use data, maybe: buffer = handle.read()

В частности, я думаю, что третье решение - единственное решение, не требующее усилий.

person Stefano Sanfilippo    schedule 12.09.2013
comment
После второго размышления и второго внимательного прочтения комментариев, которыми обменивались Кайл и Стефано, прямо под вопросом, я прошу прощения за то, что проголосовал против этого ответа. Однако похоже, что Кайл хотел спросить не то, что он на самом деле спросил. Если вы прочитаете ответ Стефано как ответ на исходный вопрос, это будет неправдой. В любом случае, если Стефано пояснит, на что он ответил (и отредактирует ответ, чтобы я мог забрать свой отрицательный голос), я буду рад внести поправку. - person nickie; 03.10.2014