在Python中下载、解压并读取gzip文件

8 投票
4 回答
28558 浏览
提问于 2025-04-16 03:10

我想在Python中下载、解压和遍历一个文本文件,而不想创建临时文件。

基本上,就是想在Python中实现这个管道功能。

curl ftp://ftp.theseed.org/genomes/SEED/SEED.fasta.gz | gunzip | processing step

这是我的代码:

def main():
    import urllib
    import gzip

    # Download SEED database
    print 'Downloading SEED Database'
    handle = urllib.urlopen('ftp://ftp.theseed.org/genomes/SEED/SEED.fasta.gz')


    with open('SEED.fasta.gz', 'wb') as out:
        while True:
            data = handle.read(1024)
            if len(data) == 0: break
            out.write(data)

    # Extract SEED database
    handle = gzip.open('SEED.fasta.gz')
    with open('SEED.fasta', 'w') as out:
        for line in handle:
            out.write(line)

    # Filter SEED database
    pass

我不想使用process.Popen()或者其他什么方法,因为我希望这个脚本能在不同的平台上都能运行。

问题是Gzip库只接受文件名作为参数,而不接受文件句柄。使用“管道”的原因是下载步骤只占用大约5%的CPU,这样同时进行解压和处理会更快。


补充说明

“由于gzip压缩的工作原理,GzipFile需要保存它的位置,并在压缩文件中前后移动。当“文件”是来自远程服务器的字节流时,这种方式就不行了;你只能一次获取一个字节,而不能在数据流中前后移动。” - 深入Python

这就是我为什么会遇到错误的原因。

AttributeError: addinfourl instance has no attribute 'tell'

那么,curl url | gunzip | whatever是怎么工作的呢?

4 个回答

2

我在寻找从网址下载并解压一个 gzip 文件的方法时,发现了这个问题,但我没能让被接受的答案在 Python 2.7 中运行。

以下是我找到的有效方法(改编自 这里):

import urllib2
import gzip
import StringIO

def download(url):
    # Download SEED database
    out_file_path = url.split("/")[-1][:-3]
    print('Downloading SEED Database from: {}'.format(url))
    response = urllib2.urlopen(url)
    compressed_file = StringIO.StringIO(response.read())
    decompressed_file = gzip.GzipFile(fileobj=compressed_file)

    # Extract SEED database
    with open(out_file_path, 'w') as outfile:
        outfile.write(decompressed_file.read())

    # Filter SEED database
    # ...
    return

if __name__ == "__main__":    
    download("ftp://ftp.ebi.ac.uk/pub/databases/Rfam/12.0/fasta_files/RF00001.fa.gz")

我更改了目标网址,因为原来的链接已经失效:我只是找了一个从 FTP 服务器上提供的 gzip 文件,就像原问题中提到的那样。

3

这是一个用 python3 写的解决方案,它不需要使用 for 循环,并且直接将 byte 对象作为 binary 流写入:

import gzip
import urllib.request

    def download_file(url):
       out_file = '/path/to/file'

       # Download archive
       try:
          # Read the file inside the .gz archive located at url
          with urllib.request.urlopen(url) as response:
             with gzip.GzipFile(fileobj=response) as uncompressed:
                file_content = uncompressed.read()

          # write to file in binary mode 'wb'
          with open(out_file, 'wb') as f:
             f.write(file_content)
             return 0

       except Exception as e:
          print(e)
          return 1

你可以用 retval=download_file(url) 来调用这个函数,以获取 返回值

9

只需要用 gzip.GzipFile(fileobj=handle) 这个方法就可以了。换句话说,"Gzip库只接受文件名作为参数,而不接受文件句柄" 这个说法并不完全正确。你只需要使用 fileobj= 这个命名参数就可以了。

撰写回答